Просмотр исходного кода

fix(ssr): fix hydration mismatch with adjacent text node from slots

fix vuejs/vue-loader#974
Evan You 8 лет назад
Родитель
Сommit
b080a14138

+ 13 - 6
src/core/vdom/helpers/normalize-children.js

@@ -42,20 +42,27 @@ function isTextNode (node): boolean {
 
 function normalizeArrayChildren (children: any, nestedIndex?: string): Array<VNode> {
   const res = []
-  let i, c, last
+  let i, c, lastIndex, last
   for (i = 0; i < children.length; i++) {
     c = children[i]
     if (isUndef(c) || typeof c === 'boolean') continue
-    last = res[res.length - 1]
+    lastIndex = res.length - 1
+    last = res[lastIndex]
     //  nested
-    if (Array.isArray(c)) {
-      res.push.apply(res, normalizeArrayChildren(c, `${nestedIndex || ''}_${i}`))
+    if (Array.isArray(c) && c.length > 0) {
+      c = normalizeArrayChildren(c, `${nestedIndex || ''}_${i}`)
+      // merge adjacent text nodes
+      if (isTextNode(c[0]) && isTextNode(last)) {
+        res[lastIndex] = createTextVNode(last.text + (c[0]: any).text)
+        c.shift()
+      }
+      res.push.apply(res, c)
     } else if (isPrimitive(c)) {
       if (isTextNode(last)) {
         // merge adjacent text nodes
         // this is necessary for SSR hydration because text nodes are
         // essentially merged when rendered to HTML strings
-        (last: any).text += String(c)
+        res[lastIndex] = createTextVNode(last.text + c)
       } else if (c !== '') {
         // convert primitive to vnode
         res.push(createTextVNode(c))
@@ -63,7 +70,7 @@ function normalizeArrayChildren (children: any, nestedIndex?: string): Array<VNo
     } else {
       if (isTextNode(c) && isTextNode(last)) {
         // merge adjacent text nodes
-        res[res.length - 1] = createTextVNode(last.text + c.text)
+        res[lastIndex] = createTextVNode(last.text + c.text)
       } else {
         // default key for nested array children (likely generated by v-for)
         if (isTrue(children._isVList) &&

+ 19 - 0
test/unit/modules/vdom/patch/hydration.spec.js

@@ -323,4 +323,23 @@ describe('vdom patch: hydration', () => {
 
     expect('not matching server-rendered content').toHaveBeenWarned()
   })
+
+  it('should hydrate with adjacent text nodes from array children (e.g. slots)', () => {
+    const dom = createMockSSRDOM('<div>foo</div> hello')
+
+    new Vue({
+      template: `<test>hello</test>`,
+      components: {
+        test: {
+          template: `
+            <div>
+              <div>foo</div>
+              <slot/>
+            </div>
+          `
+        }
+      }
+    }).$mount(dom)
+    expect('not matching server-rendered content').not.toHaveBeenWarned()
+  })
 })