Przeglądaj źródła

fix(slot): add a function to return the slot fallback content (#12014)

Co-authored-by: zrh122 <1229550935@qq.com>
Eduardo San Martin Morote 5 lat temu
rodzic
commit
ce457f9f4d

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

@@ -547,7 +547,7 @@ export function genComment (comment: ASTText): string {
 function genSlot (el: ASTElement, state: CodegenState): string {
   const slotName = el.slotName || '"default"'
   const children = genChildren(el, state)
-  let res = `_t(${slotName}${children ? `,${children}` : ''}`
+  let res = `_t(${slotName}${children ? `,function(){return ${children}}` : ''}`
   const attrs = el.attrs || el.dynamicAttrs
     ? genProps((el.attrs || []).concat(el.dynamicAttrs || []).map(attr => ({
         // slot props are camelized

+ 12 - 8
src/core/instance/render-helpers/render-slot.js

@@ -7,26 +7,30 @@ import { extend, warn, isObject } from 'core/util/index'
  */
 export function renderSlot (
   name: string,
-  fallback: ?Array<VNode>,
+  fallbackRender: ?((() => Array<VNode>) | Array<VNode>),
   props: ?Object,
   bindObject: ?Object
 ): ?Array<VNode> {
   const scopedSlotFn = this.$scopedSlots[name]
   let nodes
-  if (scopedSlotFn) { // scoped slot
+  if (scopedSlotFn) {
+    // scoped slot
     props = props || {}
     if (bindObject) {
       if (process.env.NODE_ENV !== 'production' && !isObject(bindObject)) {
-        warn(
-          'slot v-bind without argument expects an Object',
-          this
-        )
+        warn('slot v-bind without argument expects an Object', this)
       }
       props = extend(extend({}, bindObject), props)
     }
-    nodes = scopedSlotFn(props) || fallback
+    nodes =
+      scopedSlotFn(props) ||
+      (fallbackRender &&
+        (Array.isArray(fallbackRender) ? fallbackRender : fallbackRender()))
   } else {
-    nodes = this.$slots[name] || fallback
+    nodes =
+      this.$slots[name] ||
+      (fallbackRender &&
+        (Array.isArray(fallbackRender) ? fallbackRender : fallbackRender()))
   }
 
   const target = props && props.slot

+ 41 - 0
test/unit/features/component/component-slot.spec.js

@@ -109,6 +109,47 @@ describe('Component slot', () => {
     expect(child.$el.children[1].textContent).toBe('slot b')
   })
 
+ it('it should work with previous versions of the templates', () => {
+   const Test = {
+      render() {
+        var _vm = this;
+        var _h = _vm.$createElement;
+        var _c = _vm._self._c || vm._h;
+        return _c('div', [_vm._t("default", [_c('p', [_vm._v("slot default")])])], 2)
+      }
+    }
+    let vm = new Vue({
+      template: `<test/>`,
+      components: { Test }
+    }).$mount()
+    expect(vm.$el.textContent).toBe('slot default')
+    vm = new Vue({
+      template: `<test>custom content</test>`,
+      components: { Test }
+    }).$mount()
+    expect(vm.$el.textContent).toBe('custom content')
+  })
+
+  it('fallback content should not be evaluated when the parent is providing it', () => {
+    const test = jasmine.createSpy('test')
+    const vm = new Vue({
+      template: '<test>slot default</test>',
+      components: {
+        test: {
+          template: '<div><slot>{{test()}}</slot></div>',
+          methods: {
+            test () {
+              test()
+              return 'test'
+            }
+          }
+        }
+      }
+    }).$mount()
+    expect(vm.$el.textContent).toBe('slot default')
+    expect(test).not.toHaveBeenCalled()
+  })
+
   it('selector matching multiple elements', () => {
     mount({
       childTemplate: '<div><slot name="t"></slot></div>',

+ 1 - 1
test/unit/modules/compiler/codegen.spec.js

@@ -196,7 +196,7 @@ describe('codegen', () => {
   it('generate slot fallback content', () => {
     assertCodegen(
       '<div><slot><div>hi</div></slot></div>',
-      `with(this){return _c('div',[_t("default",[_c('div',[_v("hi")])])],2)}`
+      `with(this){return _c('div',[_t("default",function(){return [_c('div',[_v("hi")])]})],2)}`
     )
   })