From 6a2994e8f07d24ff6d94507a3e8effd22e2434d9 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 11 Jan 2019 23:04:04 -0500 Subject: [PATCH] revert: feat: support scoped-slot usage with $slot This reverts commit 7988a5541cd6e9a9be28b4905d69c44ddcb3878e. --- flow/compiler.js | 9 +- src/compiler/codegen/index.js | 2 +- src/compiler/optimizer.js | 3 +- src/compiler/parser/index.js | 83 ++--------------- .../component/component-scoped-slot.spec.js | 88 ------------------- 5 files changed, 10 insertions(+), 175 deletions(-) diff --git a/flow/compiler.js b/flow/compiler.js index 509633e3..de1e2df9 100644 --- a/flow/compiler.js +++ b/flow/compiler.js @@ -85,7 +85,7 @@ declare type ASTDirective = { end?: number; }; -declare type ASTNode = ASTElement | ASTText | ASTExpression +declare type ASTNode = ASTElement | ASTText | ASTExpression; declare type ASTElement = { type: 1; @@ -167,9 +167,6 @@ declare type ASTElement = { // weex specific appendAsTree?: boolean; - - // 2.6 $slot check - has$Slot?: boolean }; declare type ASTExpression = { @@ -182,8 +179,6 @@ declare type ASTExpression = { ssrOptimizability?: number; start?: number; end?: number; - // 2.6 $slot check - has$Slot?: boolean }; declare type ASTText = { @@ -195,8 +190,6 @@ declare type ASTText = { ssrOptimizability?: number; start?: number; end?: number; - // 2.6 $slot check - has$Slot?: boolean }; // SFC-parser related declarations diff --git a/src/compiler/codegen/index.js b/src/compiler/codegen/index.js index bdd76be0..d09bacdf 100644 --- a/src/compiler/codegen/index.js +++ b/src/compiler/codegen/index.js @@ -27,7 +27,7 @@ export class CodegenState { this.dataGenFns = pluckModuleFunction(options.modules, 'genData') this.directives = extend(extend({}, baseDirectives), options.directives) const isReservedTag = options.isReservedTag || no - this.maybeComponent = (el: ASTElement) => !!el.component || !isReservedTag(el.tag) + this.maybeComponent = (el: ASTElement) => el.component || !isReservedTag(el.tag) this.onceId = 0 this.staticRenderFns = [] this.pre = false diff --git a/src/compiler/optimizer.js b/src/compiler/optimizer.js index 39b8737b..5f56e74c 100644 --- a/src/compiler/optimizer.js +++ b/src/compiler/optimizer.js @@ -30,7 +30,7 @@ export function optimize (root: ?ASTElement, options: CompilerOptions) { function genStaticKeys (keys: string): Function { return makeMap( - 'type,tag,attrsList,attrsMap,plain,parent,children,attrs,start,end,rawAttrsMap,has$Slot' + + 'type,tag,attrsList,attrsMap,plain,parent,children,attrs,start,end,rawAttrsMap' + (keys ? ',' + keys : '') ) } @@ -43,7 +43,6 @@ function markStatic (node: ASTNode) { // 2. static slot content fails for hot-reloading if ( !isPlatformReservedTag(node.tag) && - !node.component && node.tag !== 'slot' && node.attrsMap['inline-template'] == null ) { diff --git a/src/compiler/parser/index.js b/src/compiler/parser/index.js index a9a0df9d..89ea5fdc 100644 --- a/src/compiler/parser/index.js +++ b/src/compiler/parser/index.js @@ -5,7 +5,7 @@ import { parseHTML } from './html-parser' import { parseText } from './text-parser' import { parseFilters } from './filter-parser' import { genAssignmentCode } from '../directives/model' -import { extend, cached, no, camelize, hyphenate, hasOwn } from 'shared/util' +import { extend, cached, no, camelize, hyphenate } from 'shared/util' import { isIE, isEdge, isServerRendering } from 'core/util/env' import { @@ -45,7 +45,6 @@ let postTransforms let platformIsPreTag let platformMustUseProp let platformGetTagNamespace -let maybeComponent export function createASTElement ( tag: string, @@ -75,8 +74,6 @@ export function parse ( platformIsPreTag = options.isPreTag || no platformMustUseProp = options.mustUseProp || no platformGetTagNamespace = options.getTagNamespace || no - const isReservedTag = options.isReservedTag || no - maybeComponent = (el: ASTElement) => !!el.component || !isReservedTag(el.tag) transforms = pluckModuleFunction(options.modules, 'transformNode') preTransforms = pluckModuleFunction(options.modules, 'preTransformNode') @@ -102,7 +99,7 @@ export function parse ( function closeElement (element) { if (!inVPre && !element.processed) { - element = processElement(element, options) + element = processElement(element, options, currentParent) } // tree management if (!stack.length && element !== root) { @@ -156,7 +153,7 @@ export function parse ( { start: el.start } ) } - if (hasOwn(el.attrsMap, 'v-for')) { + if (el.attrsMap.hasOwnProperty('v-for')) { warnOnce( 'Cannot use v-for on stateful component root element because ' + 'it renders multiple elements.', @@ -380,7 +377,8 @@ function processRawAttrs (el) { export function processElement ( element: ASTElement, - options: CompilerOptions + options: CompilerOptions, + parent: ASTElement | undefined ) { processKey(element) @@ -393,7 +391,7 @@ export function processElement ( ) processRef(element) - processSlot(element) + processSlot(element, parent) processComponent(element) for (let i = 0; i < transforms.length; i++) { element = transforms[i](element, options) || element @@ -584,86 +582,19 @@ function processSlot (el) { ) } el.slotScope = slotScope - if (process.env.NODE_ENV !== 'production' && nodeHas$Slot(el)) { - warn('Unepxected mixed usage of `slot-scope` and `$slot`.', el) - } - } else { - // 2.6 $slot support - // Context: https://github.com/vuejs/vue/issues/9180 - // Ideally, all slots should be compiled as functions (this is what we - // are doing in 3.x), but for 2.x e want to preserve complete backwards - // compatibility, and maintain the exact same compilation output for any - // code that does not use the new syntax. - - // recursively check component children for presence of `$slot` in all - // expressions until running into a nested child component. - if (maybeComponent(el) && childrenHas$Slot(el)) { - processScopedSlots(el) - } } const slotTarget = getBindingAttr(el, 'slot') if (slotTarget) { el.slotTarget = slotTarget === '""' ? '"default"' : slotTarget // preserve slot as an attribute for native shadow DOM compat // only for non-scoped slots. - if (el.tag !== 'template' && !el.slotScope && !nodeHas$Slot(el)) { + if (el.tag !== 'template' && !el.slotScope) { addAttr(el, 'slot', slotTarget, getRawBindingAttr(el, 'slot')) } } } } -function childrenHas$Slot (el): boolean { - return el.children ? el.children.some(nodeHas$Slot) : false -} - -const $slotRE = /(^|[^\w_$])\$slot($|[^\w_$])/ -function nodeHas$Slot (node): boolean { - // caching - if (hasOwn(node, 'has$Slot')) { - return (node.has$Slot: any) - } - if (node.type === 1) { // element - for (const key in node.attrsMap) { - if (dirRE.test(key) && $slotRE.test(node.attrsMap[key])) { - return (node.has$Slot = true) - } - } - return (node.has$Slot = childrenHas$Slot(node)) - } else if (node.type === 2) { // expression - // TODO more robust logic for checking $slot usage - return (node.has$Slot = $slotRE.test(node.expression)) - } - return false -} - -function processScopedSlots (el) { - // 1. group children by slot target - const groups: any = {} - for (let i = 0; i < el.children.length; i++) { - const child = el.children[i] - const target = child.slotTarget || '"default"' - if (!groups[target]) { - groups[target] = [] - } - groups[target].push(child) - } - // 2. for each slot group, check if the group contains $slot - for (const name in groups) { - const group = groups[name] - if (group.some(nodeHas$Slot)) { - // 3. if a group contains $slot, all nodes in that group gets assigned - // as a scoped slot to el and removed from children - el.plain = false - const slots = el.scopedSlots || (el.scopedSlots = {}) - const slotContainer = slots[name] = createASTElement('template', [], el) - slotContainer.children = group - slotContainer.slotScope = '$slot' - el.children = el.children.filter(c => group.indexOf(c) === -1) - } - } -} - function processComponent (el) { let binding if ((binding = getBindingAttr(el, 'is'))) { diff --git a/test/unit/features/component/component-scoped-slot.spec.js b/test/unit/features/component/component-scoped-slot.spec.js index 0956dd66..1e08b4d1 100644 --- a/test/unit/features/component/component-scoped-slot.spec.js +++ b/test/unit/features/component/component-scoped-slot.spec.js @@ -631,92 +631,4 @@ describe('Component scoped slot', () => { expect(vm.$el.innerHTML).toBe('

hello

') }).then(done) }) - - // 2.6 $slot usage - describe('$slot support', () => { - it('should work', () => { - const vm = new Vue({ - template: `
{{$slot.foo}}
`, - components: { foo: { template: `
` }} - }).$mount() - expect(vm.$el.innerHTML).toBe(`
hello
`) - }) - - it('should work for use of $slots in attributes', () => { - const vm = new Vue({ - template: `
`, - components: { foo: { template: `
` }} - }).$mount() - expect(vm.$el.innerHTML).toBe(`
`) - }) - - it('should work for root text nodes', () => { - const vm = new Vue({ - template: `{{$slot.foo}}`, - components: { foo: { template: `
` }} - }).$mount() - expect(vm.$el.innerHTML).toBe(`hello`) - }) - - it('should work for mix of root text nodes and elements', () => { - const vm = new Vue({ - template: `hi
{{ $slot.foo }}
{{$slot.foo}}
`, - components: { foo: { template: `
` }} - }).$mount() - expect(vm.$el.innerHTML).toBe(`hi
hello
hello`) - }) - - it('should work for named slots', () => { - const vm = new Vue({ - template: `
{{ $slot.foo }}
`, - components: { foo: { template: `
` }} - }).$mount() - expect(vm.$el.innerHTML).toBe(`
hello
`) - }) - - it('should work for mixed default and named slots', () => { - const vm = new Vue({ - template: `{{ $slot.foo }}
{{ $slot.foo }}
{{ $slot.foo }}
`, - components: { foo: { template: `
` }} - }).$mount() - expect(vm.$el.innerHTML).toBe(`default
default
foo
`) - }) - - it('should work for mixed $slot and non-$slot slots', () => { - const vm = new Vue({ - template: `{{ $slot.foo }}
static
{{ $slot.foo }}
`, - components: { foo: { template: `
` }} - }).$mount() - expect(vm.$el.innerHTML).toBe(`default
default
static
`) - }) - - // testing $slot detection: bracket access, using $slot alone, passing as arguments... - it('should work for alternative $slot usage', () => { - const vm = new Vue({ - template: `{{ $slot['foo'] }}
{{ $slot }}
{{ pick($slot) }}
`, - methods: { pick: s => s.foo }, - components: { foo: { template: `
` }} - }).$mount() - expect(vm.$el.innerHTML).toBe(`default
default
{}
`) - }) - - // should not consider detection if $slot is inside longer valid identifier - it('should not break when template expression uses $slots', () => { - const vm = new Vue({ - data: { some$slot: 123 }, - template: `{{ some$slot }}
{{ $slots }}
`, - components: { - foo: { - render(h) { - // should be compiled as normal slots - expect(this.$slots.default).toBeTruthy() - expect(this.$slots.foo).toBeTruthy() - return h('div', [this.$slots.default, this.$slots.foo]) - } - } - } - }).$mount() - expect(vm.$el.innerHTML).toBe(`123
{}
`) - }) - }) })