]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(sfc/scoped-style): inherit scopeId through nested HOCs with inheritAttrs: false
authorEvan You <yyx990803@gmail.com>
Tue, 1 Sep 2020 22:56:02 +0000 (18:56 -0400)
committerEvan You <yyx990803@gmail.com>
Tue, 1 Sep 2020 22:56:02 +0000 (18:56 -0400)
fix #1988

packages/runtime-core/__tests__/helpers/scopeId.spec.ts
packages/runtime-core/src/componentRenderUtils.ts
packages/runtime-core/src/renderer.ts
packages/server-renderer/__tests__/renderToStream.spec.ts
packages/server-renderer/__tests__/renderToString.spec.ts
packages/server-renderer/src/render.ts

index 07ecfc7f6e8947b76c56775391de9ac28f041ef1..f570c7f0c125d1079585fdd929b33ffc2606b99b 100644 (file)
@@ -34,7 +34,7 @@ describe('scopeId runtime support', () => {
     const root = nodeOps.createElement('div')
     render(h(App), root)
     expect(serializeInner(root)).toBe(
-      `<div parent><div parent child></div></div>`
+      `<div parent><div child parent></div></div>`
     )
   })
 
@@ -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(
-      `<div parent child>` +
+      `<div child parent>` +
         `<div parent child-s></div>` +
         // component inside slot should have:
         // - scopeId from template context
         // - slotted scopeId from slot owner
         // - its own scopeId
-        `<span parent child-s child2></span>` +
+        `<span child2 parent child-s></span>` +
         `</div>`
     )
   })
+
+  // #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(`<div parent></div>`)
+  })
 })
index 2c05358d5615d1b16af1de0681b4aeff714f6eca..9c7ff21a67a5d55a6d184359b2f4c5a7193b4aaf 100644 (file)
@@ -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)) {
index cae1ffe57c14f3283e525b85f7bcd9dfdf03b969..9c69c6d40dd2a6b939d2d355e6394fe45de408de 100644 (file)
@@ -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,
index 5f7ecf6795a96d31a017fb340adbf65b400aace3..4c9466b6725515ea8e02892e0d37d5cc0331fc05 100644 (file)
@@ -595,7 +595,7 @@ describe('ssr: renderToStream', () => {
       }
 
       expect(await renderToStream(h(Parent))).toBe(
-        `<div data-v-test data-v-child><span data-v-test data-v-child-s>slot</span></div>`
+        `<div data-v-child data-v-test><span data-v-test data-v-child-s>slot</span></div>`
       )
     })
   })
index 3e2ff0cc5280d7f6c64b8535e5845869a4fc7de4..dfaab64c2c60b0d98279637a6113f1ce623c2031 100644 (file)
@@ -551,7 +551,7 @@ describe('ssr: renderToString', () => {
       }
 
       expect(await renderToString(h(Parent))).toBe(
-        `<div data-v-test data-v-child><span data-v-test data-v-child-s>slot</span></div>`
+        `<div data-v-child data-v-test><span data-v-test data-v-child-s>slot</span></div>`
       )
     })
   })
index f8f9ac0b06bd7833f1a292b6206e3a5bf5e16f73..0cb20625b099cbc235e0329fff19c680de9132e9 100644 (file)
@@ -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,