diff --git a/src/core/vdom/patch.js b/src/core/vdom/patch.js
index 799e508bd7a..a24c6237789 100644
--- a/src/core/vdom/patch.js
+++ b/src/core/vdom/patch.js
@@ -30,6 +30,7 @@ function sameVnode (vnode1, vnode2) {
return (
vnode1.key === vnode2.key &&
vnode1.tag === vnode2.tag &&
+ vnode1.isComment === vnode2.isComment &&
!vnode1.data === !vnode2.data
)
}
@@ -87,12 +88,7 @@ export function createPatchFunction (backend) {
// component also has set the placeholder vnode's elm.
// in that case we can just return the element and be done.
if (isDef(i = vnode.child)) {
- if (vnode.data.pendingInsert) {
- insertedVnodeQueue.push.apply(insertedVnodeQueue, vnode.data.pendingInsert)
- }
- vnode.elm = vnode.child.$el
- invokeCreateHooks(vnode, insertedVnodeQueue)
- setScope(vnode)
+ initComponent(vnode, insertedVnodeQueue)
return vnode.elm
}
}
@@ -127,6 +123,8 @@ export function createPatchFunction (backend) {
if (isDef(data)) {
invokeCreateHooks(vnode, insertedVnodeQueue)
}
+ } else if (vnode.isComment) {
+ elm = vnode.elm = nodeOps.createComment(vnode.text)
} else {
elm = vnode.elm = nodeOps.createTextNode(vnode.text)
}
@@ -144,6 +142,15 @@ export function createPatchFunction (backend) {
}
}
+ function initComponent (vnode, insertedVnodeQueue) {
+ if (vnode.data.pendingInsert) {
+ insertedVnodeQueue.push.apply(insertedVnodeQueue, vnode.data.pendingInsert)
+ }
+ vnode.elm = vnode.child.$el
+ invokeCreateHooks(vnode, insertedVnodeQueue)
+ setScope(vnode)
+ }
+
// set scope id attribute for scoped CSS.
// this is implemented as a special case to avoid the overhead
// of going through the normal attribute patching process.
@@ -360,7 +367,7 @@ export function createPatchFunction (backend) {
if (isDef(i = data.hook) && isDef(i = i.init)) i(vnode, true /* hydrating */)
if (isDef(i = vnode.child)) {
// child component. it should have hydrated its own tree.
- invokeCreateHooks(vnode, insertedVnodeQueue)
+ initComponent(vnode, insertedVnodeQueue)
return true
}
}
@@ -425,7 +432,7 @@ export function createPatchFunction (backend) {
// mounting to a real element
// check if this is server-rendered content and if we can perform
// a successful hydration.
- if (oldVnode.hasAttribute('server-rendered')) {
+ if (oldVnode.nodeType === 1 && oldVnode.hasAttribute('server-rendered')) {
oldVnode.removeAttribute('server-rendered')
hydrating = true
}
diff --git a/src/core/vdom/vnode.js b/src/core/vdom/vnode.js
index 08d17f0f577..bcd8737acee 100644
--- a/src/core/vdom/vnode.js
+++ b/src/core/vdom/vnode.js
@@ -16,6 +16,7 @@ export default class VNode {
raw: ?boolean; // contains raw HTML
isStatic: ?boolean; // hoisted static node
isRootInsert: boolean; // necessary for enter transition check
+ isComment: boolean;
constructor (
tag?: string,
@@ -43,6 +44,7 @@ export default class VNode {
this.raw = false
this.isStatic = false
this.isRootInsert = true
+ this.isComment = false
// apply construct hook.
// this is applied during render, before patch happens.
// unlike other hooks, this is applied on both client and server.
@@ -53,4 +55,9 @@ export default class VNode {
}
}
-export const emptyVNode = () => new VNode(undefined, undefined, undefined, '')
+export const emptyVNode = () => {
+ const node = new VNode()
+ node.text = ''
+ node.isComment = true
+ return node
+}
diff --git a/src/platforms/web/runtime/node-ops.js b/src/platforms/web/runtime/node-ops.js
index 281f3155139..fb2def45f5c 100644
--- a/src/platforms/web/runtime/node-ops.js
+++ b/src/platforms/web/runtime/node-ops.js
@@ -14,6 +14,10 @@ export function createTextNode (text: string): Text {
return document.createTextNode(text)
}
+export function createComment (text: string): Comment {
+ return document.createComment(text)
+}
+
export function insertBefore (parentNode: Node, newNode: Node, referenceNode: Node) {
parentNode.insertBefore(newNode, referenceNode)
}
diff --git a/src/server/render.js b/src/server/render.js
index 3db57ef4086..b25b70d34a9 100644
--- a/src/server/render.js
+++ b/src/server/render.js
@@ -83,6 +83,8 @@ export function createRenderFunction (
} else {
if (node.tag) {
renderElement(node, write, next, isRoot)
+ } else if (node.isComment) {
+ write(``, next)
} else {
write(node.raw ? node.text : encodeHTML(String(node.text)), next)
}
diff --git a/test/ssr/ssr-string.spec.js b/test/ssr/ssr-string.spec.js
index 1252df10b25..e11039e8593 100644
--- a/test/ssr/ssr-string.spec.js
+++ b/test/ssr/ssr-string.spec.js
@@ -500,6 +500,15 @@ describe('SSR: renderToString', () => {
})
})
+ it('comment nodes', done => {
+ renderVmWithOptions({
+ template: '
'
+ }, result => {
+ expect(result).toContain(``)
+ done()
+ })
+ })
+
it('should catch error', done => {
renderToString(new Vue({
render () {
diff --git a/test/unit/features/component/component-async.spec.js b/test/unit/features/component/component-async.spec.js
index ce13deaa732..8d997050adb 100644
--- a/test/unit/features/component/component-async.spec.js
+++ b/test/unit/features/component/component-async.spec.js
@@ -40,7 +40,7 @@ describe('Component async', () => {
}
}
}).$mount()
- expect(vm.$el.nodeType).toBe(3)
+ expect(vm.$el.nodeType).toBe(8)
expect(vm.$children.length).toBe(0)
function next () {
expect(vm.$el.nodeType).toBe(1)
diff --git a/test/unit/features/component/component-keep-alive.spec.js b/test/unit/features/component/component-keep-alive.spec.js
index 7b5a176d04d..4336475680a 100644
--- a/test/unit/features/component/component-keep-alive.spec.js
+++ b/test/unit/features/component/component-keep-alive.spec.js
@@ -105,16 +105,16 @@ describe('Component keep-alive', () => {
vm.view = 'two'
waitForUpdate(() => {
expect(vm.$el.innerHTML).toBe(
- 'one
'
+ 'one
'
)
assertHookCalls(one, [1, 1, 1, 1, 0])
assertHookCalls(two, [0, 0, 0, 0, 0])
}).thenWaitFor(nextFrame).then(() => {
expect(vm.$el.innerHTML).toBe(
- 'one
'
+ 'one
'
)
}).thenWaitFor(_next => { next = _next }).then(() => {
- expect(vm.$el.innerHTML).toBe('')
+ expect(vm.$el.innerHTML).toBe('')
}).thenWaitFor(nextFrame).then(() => {
expect(vm.$el.innerHTML).toBe(
'two
'
@@ -135,16 +135,16 @@ describe('Component keep-alive', () => {
vm.view = 'one'
}).then(() => {
expect(vm.$el.innerHTML).toBe(
- 'two
'
+ 'two
'
)
assertHookCalls(one, [1, 1, 1, 1, 0])
assertHookCalls(two, [1, 1, 1, 1, 0])
}).thenWaitFor(nextFrame).then(() => {
expect(vm.$el.innerHTML).toBe(
- 'two
'
+ 'two
'
)
}).thenWaitFor(_next => { next = _next }).then(() => {
- expect(vm.$el.innerHTML).toBe('')
+ expect(vm.$el.innerHTML).toBe('')
}).thenWaitFor(nextFrame).then(() => {
expect(vm.$el.innerHTML).toBe(
'one
'
diff --git a/test/unit/features/component/component.spec.js b/test/unit/features/component/component.spec.js
index d0bfa1aca49..cb4a5c2c52b 100644
--- a/test/unit/features/component/component.spec.js
+++ b/test/unit/features/component/component.spec.js
@@ -107,7 +107,7 @@ describe('Component', () => {
vm.view = ''
})
.then(() => {
- expect(vm.$el.nodeType).toBe(3)
+ expect(vm.$el.nodeType).toBe(8)
expect(vm.$el.data).toBe('')
}).then(done)
})
diff --git a/test/unit/features/transition/transition-mode.spec.js b/test/unit/features/transition/transition-mode.spec.js
index e4c20495848..d4dbb7914e2 100644
--- a/test/unit/features/transition/transition-mode.spec.js
+++ b/test/unit/features/transition/transition-mode.spec.js
@@ -68,14 +68,14 @@ if (!isIE9) {
vm.view = 'two'
waitForUpdate(() => {
expect(vm.$el.innerHTML).toBe(
- 'one
'
+ 'one
'
)
}).thenWaitFor(nextFrame).then(() => {
expect(vm.$el.innerHTML).toBe(
- 'one
'
+ 'one
'
)
}).thenWaitFor(_next => { next = _next }).then(() => {
- expect(vm.$el.innerHTML).toBe('')
+ expect(vm.$el.innerHTML).toBe('')
}).thenWaitFor(nextFrame).then(() => {
expect(vm.$el.innerHTML).toBe(
'two
'
@@ -257,14 +257,14 @@ if (!isIE9) {
vm.view = 'two'
waitForUpdate(() => {
expect(vm.$el.innerHTML).toBe(
- 'one
'
+ 'one
'
)
}).thenWaitFor(nextFrame).then(() => {
expect(vm.$el.innerHTML).toBe(
- 'one
'
+ 'one
'
)
}).thenWaitFor(_next => { next = _next }).then(() => {
- expect(vm.$el.innerHTML).toBe('')
+ expect(vm.$el.innerHTML).toBe('')
}).thenWaitFor(nextFrame).then(() => {
expect(vm.$el.innerHTML).toBe(
'two
'
diff --git a/test/unit/features/transition/transition.spec.js b/test/unit/features/transition/transition.spec.js
index b47ac04d2ef..1c03a9a7cb3 100644
--- a/test/unit/features/transition/transition.spec.js
+++ b/test/unit/features/transition/transition.spec.js
@@ -316,7 +316,7 @@ if (!isIE9) {
vm.ok = false
waitForUpdate(() => {
expect(leaveSpy).toHaveBeenCalled()
- expect(vm.$el.innerHTML).toBe('')
+ expect(vm.$el.innerHTML).toBe('')
vm.ok = true
}).then(() => {
expect(enterSpy).toHaveBeenCalled()
@@ -339,9 +339,9 @@ if (!isIE9) {
vm.ok = false
waitForUpdate(() => {
expect(leaveSpy).toHaveBeenCalled()
- expect(vm.$el.innerHTML).toBe('foo
')
+ expect(vm.$el.innerHTML).toBe('foo
')
}).thenWaitFor(nextFrame).then(() => {
- expect(vm.$el.innerHTML).toBe('')
+ expect(vm.$el.innerHTML).toBe('')
vm.ok = true
}).then(() => {
expect(enterSpy).toHaveBeenCalled()
@@ -367,7 +367,7 @@ if (!isIE9) {
}
}).$mount(el)
- expect(vm.$el.innerHTML).toBe('')
+ expect(vm.$el.innerHTML).toBe('')
vm.ok = true
waitForUpdate(() => {
expect(vm.$el.children[0].className).toBe('test test-enter test-enter-active')
@@ -652,7 +652,7 @@ if (!isIE9) {
expect(vm.$el.childNodes[0].getAttribute('class')).toBe('test v-leave-active')
}).thenWaitFor(duration + 10).then(() => {
expect(vm.$el.childNodes.length).toBe(1)
- expect(vm.$el.childNodes[0].nodeType).toBe(3) // should be an empty text node
+ expect(vm.$el.childNodes[0].nodeType).toBe(8) // should be an empty comment node
expect(vm.$el.childNodes[0].textContent).toBe('')
vm.ok = true
}).then(() => {