Skip to content

Commit eef040e

Browse files
committed
vdom: teardown stale directives on patch (fix #3491)
1 parent 0f8ad06 commit eef040e

File tree

3 files changed

+33
-1
lines changed

3 files changed

+33
-1
lines changed

src/core/vdom/modules/directives.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ export default {
88
},
99
update: function updateDirectives (oldVnode: VNodeWithData, vnode: VNodeWithData) {
1010
applyDirectives(oldVnode, vnode, 'update')
11+
// if old vnode has directives but new vnode doesn't
12+
// we need to teardown the directives on the old one.
13+
if (oldVnode.data.directives && !vnode.data.directives) {
14+
applyDirectives(oldVnode, oldVnode, 'unbind')
15+
}
1116
},
1217
postpatch: function postupdateDirectives (oldVnode: VNodeWithData, vnode: VNodeWithData) {
1318
applyDirectives(oldVnode, vnode, 'componentUpdated')
@@ -27,7 +32,7 @@ function applyDirectives (
2732
const dirs = vnode.data.directives
2833
if (dirs) {
2934
const oldDirs = oldVnode.data.directives
30-
const isUpdate = hook === 'update'
35+
const isUpdate = hook === 'update' || hook === 'componentUpdated'
3136
for (let i = 0; i < dirs.length; i++) {
3237
const dir = dirs[i]
3338
const def = resolveAsset(vnode.context.$options, 'directives', dir.name, true)

src/entries/web-runtime-with-compiler.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Vue.prototype.$mount = function (
1717
): Component {
1818
el = el && query(el)
1919

20+
/* istanbul ignore if */
2021
if (el === document.body || el === document.documentElement) {
2122
process.env.NODE_ENV !== 'production' && warn(
2223
`Do not mount Vue to <html> or <body> - mount to normal elements instead.`

test/unit/features/options/directives.spec.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,32 @@ describe('Options directives', () => {
114114
}).then(done)
115115
})
116116

117+
it('should teardown directives on old vnodes when new vnodes have none', done => {
118+
const vm = new Vue({
119+
data: {
120+
ok: true
121+
},
122+
template: `
123+
<div>
124+
<div v-if="ok" v-test>a</div>
125+
<div v-else class="b">b</div>
126+
</div>
127+
`,
128+
directives: {
129+
test: {
130+
bind: el => { el.id = 'a' },
131+
unbind: el => { el.id = '' }
132+
}
133+
}
134+
}).$mount()
135+
expect(vm.$el.children[0].id).toBe('a')
136+
vm.ok = false
137+
waitForUpdate(() => {
138+
expect(vm.$el.children[0].id).toBe('')
139+
expect(vm.$el.children[0].className).toBe('b')
140+
}).then(done)
141+
})
142+
117143
it('warn non-existent', () => {
118144
new Vue({
119145
template: '<div v-test></div>'

0 commit comments

Comments
 (0)