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

fix vuejs/vue-loader#974
This commit is contained in:
Evan You 2017-09-14 12:25:22 -04:00
parent 94512f3e8c
commit b080a14138
2 changed files with 32 additions and 6 deletions

View File

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

View File

@ -323,4 +323,23 @@ describe('vdom patch: hydration', () => {
expect('not matching server-rendered content').toHaveBeenWarned() 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()
})
}) })