Răsfoiți Sursa

support default scoped slot + function children as scoped slot

Evan You 9 ani în urmă
părinte
comite
303378f85b

+ 1 - 0
.babelrc

@@ -1,5 +1,6 @@
 {
   "presets": ["es2015", "flow-vue"],
+  "plugins": ["transform-vue-jsx"],
   "ignore": [
     "dist/*.js",
     "packages/**/*.js"

+ 1 - 0
flow/vnode.js

@@ -53,6 +53,7 @@ declare interface VNodeData {
   };
   directives?: Array<VNodeDirective>;
   keepAlive?: boolean;
+  scopedSlots?: { [key: string]: Function }
 }
 
 declare type VNodeDirective = {

+ 3 - 0
package.json

@@ -60,8 +60,11 @@
   "devDependencies": {
     "babel-core": "^6.9.0",
     "babel-eslint": "^7.1.0",
+    "babel-helper-vue-jsx-merge-props": "^2.0.2",
     "babel-loader": "^6.2.4",
     "babel-plugin-coverage": "^1.0.0",
+    "babel-plugin-syntax-jsx": "^6.18.0",
+    "babel-plugin-transform-vue-jsx": "^3.2.0",
     "babel-preset-es2015": "^6.9.0",
     "babel-preset-flow-vue": "^1.0.0",
     "buble": "^0.14.0",

+ 1 - 1
src/compiler/parser/index.js

@@ -355,7 +355,7 @@ function processSlot (el) {
   } else {
     const slotTarget = getBindingAttr(el, 'slot')
     if (slotTarget) {
-      el.slotTarget = slotTarget
+      el.slotTarget = slotTarget === '""' ? '"default"' : slotTarget
       el.slotScope = getAndRemoveAttr(el, 'scope')
     }
   }

+ 0 - 3
src/core/instance/render.js

@@ -191,9 +191,6 @@ export function renderMixin (Vue: Class<Component>) {
     fallback: ?Array<VNode>,
     props: ?Object
   ): ?Array<VNode> {
-    if (process.env.NODE_ENV !== 'production' && name === 'default' && props) {
-      warn(`Scoped slots must be named`, this)
-    }
     const scopedSlotFn = this.$scopedSlots && this.$scopedSlots[name]
     if (scopedSlotFn) { // scoped slot
       return scopedSlotFn(props || {}) || fallback

+ 7 - 0
src/core/vdom/create-element.js

@@ -39,6 +39,13 @@ export function _createElement (
     // in case of component :is set to falsy value
     return emptyVNode()
   }
+  // support single function children as default scoped slot
+  if (Array.isArray(children) &&
+      typeof children[0] === 'function') {
+    data = data || {}
+    data.scopedSlots = { default: children[0] }
+    children.length = 0
+  }
   if (typeof tag === 'string') {
     let Ctor
     const ns = config.getTagNamespace(tag)

+ 66 - 20
test/unit/features/component/component-scoped-slot.spec.js

@@ -1,6 +1,25 @@
 import Vue from 'vue'
 
 describe('Component scoped slot', () => {
+  it('default slot', () => {
+    const vm = new Vue({
+      template: `<test><span slot scope="props">{{ props.msg }}</span></test>`,
+      components: {
+        test: {
+          data () {
+            return { msg: 'hello' }
+          },
+          template: `
+            <div>
+              <slot :msg="msg"></slot>
+            </div>
+          `
+        }
+      }
+    }).$mount()
+    expect(vm.$el.innerHTML).toBe('<span>hello</span>')
+  })
+
   it('normal element slot', done => {
     const vm = new Vue({
       template: `
@@ -204,25 +223,6 @@ describe('Component scoped slot', () => {
     expect(vm.$el.innerHTML).toBe('<span>meh</span>')
   })
 
-  it('warn un-named scoped slot', () => {
-    new Vue({
-      template: `<test><span scope="lol"></span></test>`,
-      components: {
-        test: {
-          data () {
-            return { msg: 'hello' }
-          },
-          template: `
-            <div>
-              <slot :msg="msg"></slot>
-            </div>
-          `
-        }
-      }
-    }).$mount()
-    expect('Scoped slots must be named').toHaveBeenWarned()
-  })
-
   it('warn key on slot', () => {
     new Vue({
       template: `
@@ -250,7 +250,7 @@ describe('Component scoped slot', () => {
     expect(`\`key\` does not work on <slot>`).toHaveBeenWarned()
   })
 
-  it('render function usage', done => {
+  it('render function usage (named, via data)', done => {
     const vm = new Vue({
       render (h) {
         return h('test', {
@@ -282,4 +282,50 @@ describe('Component scoped slot', () => {
       expect(vm.$el.innerHTML).toBe('<span>world</span>')
     }).then(done)
   })
+
+  it('render function usage (default, as children)', () => {
+    const vm = new Vue({
+      render (h) {
+        return h('test', [
+          props => h('span', [props.msg])
+        ])
+      },
+      components: {
+        test: {
+          data () {
+            return { msg: 'hello' }
+          },
+          render (h) {
+            return h('div', [
+              this.$scopedSlots.default({ msg: this.msg })
+            ])
+          }
+        }
+      }
+    }).$mount()
+    expect(vm.$el.innerHTML).toBe('<span>hello</span>')
+  })
+
+  it('render function usage (JSX)', () => {
+    const vm = new Vue({
+      render (h) {
+        return <test>{
+          props => <span>{props.msg}</span>
+        }</test>
+      },
+      components: {
+        test: {
+          data () {
+            return { msg: 'hello' }
+          },
+          render (h) {
+            return <div>
+              {this.$scopedSlots.default({ msg: this.msg })}
+            </div>
+          }
+        }
+      }
+    }).$mount()
+    expect(vm.$el.innerHTML).toBe('<span>hello</span>')
+  })
 })