Skip to content

Commit

Permalink
fix: should warn unknown components during hydration
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 authored and hefeng committed Jan 25, 2019
1 parent fd87fe5 commit a9757e8
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 30 deletions.
67 changes: 37 additions & 30 deletions src/core/vdom/patch.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,23 @@ export function createPatchFunction (backend) {
}
}

let inPre = 0
function isUnknownElement (vnode, inVPre) {
return (
!inVPre &&
!vnode.ns &&
!(
config.ignoredElements.length &&
config.ignoredElements.some(ignore => {
return isRegExp(ignore)
? ignore.test(vnode.tag)
: ignore === vnode.tag
})
) &&
config.isUnknownElement(vnode.tag)
)
}

let creatingElmInVPre = 0
function createElm (vnode, insertedVnodeQueue, parentElm, refElm, nested) {
vnode.isRootInsert = !nested // for transition enter check
if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
Expand All @@ -116,21 +132,9 @@ export function createPatchFunction (backend) {
if (isDef(tag)) {
if (process.env.NODE_ENV !== 'production') {
if (data && data.pre) {
inPre++
creatingElmInVPre++
}
if (
!inPre &&
!vnode.ns &&
!(
config.ignoredElements.length &&
config.ignoredElements.some(ignore => {
return isRegExp(ignore)
? ignore.test(tag)
: ignore === tag
})
) &&
config.isUnknownElement(tag)
) {
if (isUnknownElement(vnode, creatingElmInVPre)) {
warn(
'Unknown custom element: <' + tag + '> - did you ' +
'register the component correctly? For recursive components, ' +
Expand Down Expand Up @@ -172,7 +176,7 @@ export function createPatchFunction (backend) {
}

if (process.env.NODE_ENV !== 'production' && data && data.pre) {
inPre--
creatingElmInVPre--
}
} else if (isTrue(vnode.isComment)) {
vnode.elm = nodeOps.createComment(vnode.text)
Expand Down Expand Up @@ -527,25 +531,28 @@ export function createPatchFunction (backend) {
}
}

let bailed = false
let hydrationBailed = false
// list of modules that can skip create hook during hydration because they
// are already rendered on the client or has no need for initialization
const isRenderedModule = makeMap('attrs,style,class,staticClass,staticStyle,key')

// Note: this is a browser-only function so we can assume elms are DOM nodes.
function hydrate (elm, vnode, insertedVnodeQueue) {
function hydrate (elm, vnode, insertedVnodeQueue, inVPre) {
let i
const { tag, data, children } = vnode
inVPre = inVPre || (data && data.pre)
vnode.elm = elm

if (isTrue(vnode.isComment) && isDef(vnode.asyncFactory)) {
vnode.elm = elm
vnode.isAsyncPlaceholder = true
return true
}
// assert node match
if (process.env.NODE_ENV !== 'production') {
if (!assertNodeMatch(elm, vnode)) {
if (!assertNodeMatch(elm, vnode, inVPre)) {
return false
}
}
vnode.elm = elm
const { tag, data, children } = vnode
if (isDef(data)) {
if (isDef(i = data.hook) && isDef(i = i.init)) i(vnode, true /* hydrating */)
if (isDef(i = vnode.componentInstance)) {
Expand All @@ -566,9 +573,9 @@ export function createPatchFunction (backend) {
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' &&
typeof console !== 'undefined' &&
!bailed
!hydrationBailed
) {
bailed = true
hydrationBailed = true
console.warn('Parent: ', elm)
console.warn('server innerHTML: ', i)
console.warn('client innerHTML: ', elm.innerHTML)
Expand All @@ -580,7 +587,7 @@ export function createPatchFunction (backend) {
let childrenMatch = true
let childNode = elm.firstChild
for (let i = 0; i < children.length; i++) {
if (!childNode || !hydrate(childNode, children[i], insertedVnodeQueue)) {
if (!childNode || !hydrate(childNode, children[i], insertedVnodeQueue, inVPre)) {
childrenMatch = false
break
}
Expand All @@ -592,9 +599,9 @@ export function createPatchFunction (backend) {
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' &&
typeof console !== 'undefined' &&
!bailed
!hydrationBailed
) {
bailed = true
hydrationBailed = true
console.warn('Parent: ', elm)
console.warn('Mismatching childNodes vs. VNodes: ', elm.childNodes, children)
}
Expand All @@ -617,10 +624,10 @@ export function createPatchFunction (backend) {
return true
}

function assertNodeMatch (node, vnode) {
function assertNodeMatch (node, vnode, inVPre) {
if (isDef(vnode.tag)) {
return (
vnode.tag.indexOf('vue-component') === 0 ||
return vnode.tag.indexOf('vue-component') === 0 || (
!isUnknownElement(vnode, inVPre) &&
vnode.tag.toLowerCase() === (node.tagName && node.tagName.toLowerCase())
)
} else {
Expand Down
11 changes: 11 additions & 0 deletions test/unit/modules/vdom/patch/hydration.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,17 @@ describe('vdom patch: hydration', () => {
expect('not matching server-rendered content').toHaveBeenWarned()
})

it('should warn failed hydration when component is not properly registered', () => {
const dom = createMockSSRDOM('<div><foo></foo></div>')

new Vue({
template: '<div><foo></foo></div>'
}).$mount(dom)

expect('not matching server-rendered content').toHaveBeenWarned()
expect('Unknown custom element: <foo>').toHaveBeenWarned()
})

it('should overwrite textNodes in the correct position but with mismatching text without warning', () => {
const dom = createMockSSRDOM('<div><span>foo</span></div>')

Expand Down

0 comments on commit a9757e8

Please sign in to comment.