From c0427b45ffebb4cda24de16fa6365a65e185d2d7 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 1 Sep 2020 18:56:02 -0400 Subject: [PATCH] fix(sfc/scoped-style): inherit scopeId through nested HOCs with inheritAttrs: false fix #1988 --- .../__tests__/helpers/scopeId.spec.ts | 31 +++++++++- .../runtime-core/src/componentRenderUtils.ts | 17 ------ packages/runtime-core/src/renderer.ts | 61 ++++++++++++++++--- .../__tests__/renderToStream.spec.ts | 2 +- .../__tests__/renderToString.spec.ts | 2 +- packages/server-renderer/src/render.ts | 49 +++++++++++---- 6 files changed, 120 insertions(+), 42 deletions(-) diff --git a/packages/runtime-core/__tests__/helpers/scopeId.spec.ts b/packages/runtime-core/__tests__/helpers/scopeId.spec.ts index 07ecfc7f6e8..f570c7f0c12 100644 --- a/packages/runtime-core/__tests__/helpers/scopeId.spec.ts +++ b/packages/runtime-core/__tests__/helpers/scopeId.spec.ts @@ -34,7 +34,7 @@ describe('scopeId runtime support', () => { const root = nodeOps.createElement('div') render(h(App), root) expect(serializeInner(root)).toBe( - `
` + `
` ) }) @@ -67,14 +67,39 @@ describe('scopeId runtime support', () => { // - scopeId from parent // - slotted scopeId (with `-s` postfix) from child (the tree owner) expect(serializeInner(root)).toBe( - `
` + + `
` + `
` + // component inside slot should have: // - scopeId from template context // - slotted scopeId from slot owner // - its own scopeId - `` + + `` + `
` ) }) + + // #1988 + test('should inherit scopeId through nested HOCs with inheritAttrs: false', () => { + const withParentId = withScopeId('parent') + const App = { + __scopeId: 'parent', + render: withParentId(() => { + return h(Child) + }) + } + + function Child() { + return h(Child2, { class: 'foo' }) + } + + function Child2() { + return h('div') + } + Child2.inheritAttrs = false + + const root = nodeOps.createElement('div') + render(h(App), root) + + expect(serializeInner(root)).toBe(`
`) + }) }) diff --git a/packages/runtime-core/src/componentRenderUtils.ts b/packages/runtime-core/src/componentRenderUtils.ts index 2c05358d561..9c7ff21a67a 100644 --- a/packages/runtime-core/src/componentRenderUtils.ts +++ b/packages/runtime-core/src/componentRenderUtils.ts @@ -42,7 +42,6 @@ export function renderComponentRoot( ): VNode { const { type: Component, - parent, vnode, proxy, withProxy, @@ -172,22 +171,6 @@ export function renderComponentRoot( } } - // inherit scopeId - const scopeId = vnode.scopeId - // vite#536: if subtree root is created from parent slot if would already - // have the correct scopeId, in this case adding the scopeId will cause - // it to be removed if the original slot vnode is reused. - const needScopeId = scopeId && root.scopeId !== scopeId - const treeOwnerId = parent && parent.type.__scopeId - const slotScopeId = - treeOwnerId && treeOwnerId !== scopeId ? treeOwnerId + '-s' : null - if (needScopeId || slotScopeId) { - const extras: Data = {} - if (needScopeId) extras[scopeId!] = '' - if (slotScopeId) extras[slotScopeId] = '' - root = cloneVNode(root, extras) - } - // inherit directives if (vnode.dirs) { if (__DEV__ && !isElementRoot(root)) { diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index cae1ffe57c1..9c69c6d40dd 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -745,15 +745,31 @@ function baseCreateRenderer( } } // scopeId - if (scopeId) { - hostSetScopeId(el, scopeId) - } - const treeOwnerId = parentComponent && parentComponent.type.__scopeId - // vnode's own scopeId and the current patched component's scopeId is - // different - this is a slot content node. - if (treeOwnerId && treeOwnerId !== scopeId) { - hostSetScopeId(el, treeOwnerId + '-s') - } + setScopeId(el, scopeId, vnode, parentComponent) + // if (scopeId) { + // hostSetScopeId(el, scopeId) + // } + // if (parentComponent) { + // const treeOwnerId = parentComponent.type.__scopeId + // // vnode's own scopeId and the current patched component's scopeId is + // // different - this is a slot content node. + // if (treeOwnerId && treeOwnerId !== scopeId) { + // hostSetScopeId(el, treeOwnerId + '-s') + // } + // const parentScopeId = + // vnode === parentComponent.subTree && parentComponent.vnode.scopeId + // if (parentScopeId) { + // hostSetScopeId(el, parentScopeId) + // if (parentComponent.parent) { + // const treeOwnerId = parentComponent.parent.type.__scopeId + // // vnode's own scopeId and the current patched component's scopeId is + // // different - this is a slot content node. + // if (treeOwnerId && treeOwnerId !== parentScopeId) { + // hostSetScopeId(el, treeOwnerId + '-s') + // } + // } + // } + // } } if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) { Object.defineProperty(el, '__vnode', { @@ -791,6 +807,33 @@ function baseCreateRenderer( } } + const setScopeId = ( + el: RendererElement, + scopeId: string | false | null, + vnode: VNode, + parentComponent: ComponentInternalInstance | null + ) => { + if (scopeId) { + hostSetScopeId(el, scopeId) + } + if (parentComponent) { + const treeOwnerId = parentComponent.type.__scopeId + // vnode's own scopeId and the current patched component's scopeId is + // different - this is a slot content node. + if (treeOwnerId && treeOwnerId !== scopeId) { + hostSetScopeId(el, treeOwnerId + '-s') + } + if (vnode === parentComponent.subTree) { + setScopeId( + el, + parentComponent.vnode.scopeId, + parentComponent.vnode, + parentComponent.parent + ) + } + } + } + const mountChildren: MountChildrenFn = ( children, container, diff --git a/packages/server-renderer/__tests__/renderToStream.spec.ts b/packages/server-renderer/__tests__/renderToStream.spec.ts index 5f7ecf6795a..4c9466b6725 100644 --- a/packages/server-renderer/__tests__/renderToStream.spec.ts +++ b/packages/server-renderer/__tests__/renderToStream.spec.ts @@ -595,7 +595,7 @@ describe('ssr: renderToStream', () => { } expect(await renderToStream(h(Parent))).toBe( - `
slot
` + `
slot
` ) }) }) diff --git a/packages/server-renderer/__tests__/renderToString.spec.ts b/packages/server-renderer/__tests__/renderToString.spec.ts index 3e2ff0cc528..dfaab64c2c6 100644 --- a/packages/server-renderer/__tests__/renderToString.spec.ts +++ b/packages/server-renderer/__tests__/renderToString.spec.ts @@ -551,7 +551,7 @@ describe('ssr: renderToString', () => { } expect(await renderToString(h(Parent))).toBe( - `
slot
` + `
slot
` ) }) }) diff --git a/packages/server-renderer/src/render.ts b/packages/server-renderer/src/render.ts index f8f9ac0b06b..0cb20625b09 100644 --- a/packages/server-renderer/src/render.ts +++ b/packages/server-renderer/src/render.ts @@ -101,7 +101,11 @@ function renderComponentSubTree( const comp = instance.type as Component const { getBuffer, push } = createBuffer() if (isFunction(comp)) { - renderVNode(push, renderComponentRoot(instance), instance) + renderVNode( + push, + (instance.subTree = renderComponentRoot(instance)), + instance + ) } else { if (!instance.render && !comp.ssrRender && isString(comp.template)) { comp.ssrRender = ssrCompile(comp.template, instance) @@ -139,7 +143,11 @@ function renderComponentSubTree( ) setCurrentRenderingInstance(null) } else if (instance.render) { - renderVNode(push, renderComponentRoot(instance), instance) + renderVNode( + push, + (instance.subTree = renderComponentRoot(instance)), + instance + ) } else { warn( `Component ${ @@ -225,15 +233,7 @@ function renderElementVNode( openTag += ssrRenderAttrs(props, tag) } - if (scopeId) { - openTag += ` ${scopeId}` - const treeOwnerId = parentComponent && parentComponent.type.__scopeId - // vnode's own scopeId and the current rendering component's scopeId is - // different - this is a slot content node. - if (treeOwnerId && treeOwnerId !== scopeId) { - openTag += ` ${treeOwnerId}-s` - } - } + openTag += resolveScopeId(scopeId, vnode, parentComponent) push(openTag + `>`) if (!isVoidTag(tag)) { @@ -265,6 +265,33 @@ function renderElementVNode( } } +function resolveScopeId( + scopeId: string | null, + vnode: VNode, + parentComponent: ComponentInternalInstance | null +) { + let res = `` + if (scopeId) { + res = ` ${scopeId}` + } + if (parentComponent) { + const treeOwnerId = parentComponent.type.__scopeId + // vnode's own scopeId and the current rendering component's scopeId is + // different - this is a slot content node. + if (treeOwnerId && treeOwnerId !== scopeId) { + res += ` ${treeOwnerId}-s` + } + if (vnode === parentComponent.subTree) { + res += resolveScopeId( + parentComponent.vnode.scopeId, + parentComponent.vnode, + parentComponent.parent + ) + } + } + return res +} + function applySSRDirectives( vnode: VNode, rawProps: VNodeProps | null,