Skip to content

Commit 1fa3844

Browse files
committed
refactor directive update implementation
1 parent 4a74883 commit 1fa3844

File tree

7 files changed

+113
-71
lines changed

7 files changed

+113
-71
lines changed

flow/vnode.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,10 @@ declare interface VNodeData {
5656

5757
declare type VNodeDirective = {
5858
name: string;
59+
rawName: string;
5960
value?: any;
6061
oldValue?: any;
6162
arg?: string;
6263
modifiers?: { [key: string]: boolean };
64+
def?: Object;
6365
}

src/compiler/codegen/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ function genDirectives (el: ASTElement): string | void {
187187
}
188188
if (needRuntime) {
189189
hasRuntime = true
190-
res += `{name:"${dir.name}"${
190+
res += `{name:"${dir.name}",rawName:"${dir.rawName}"${
191191
dir.value ? `,value:(${dir.value}),expression:${JSON.stringify(dir.value)}` : ''
192192
}${
193193
dir.arg ? `,arg:"${dir.arg}"` : ''

src/compiler/helpers.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@ export function addAttr (el: ASTElement, name: string, value: string) {
2424
export function addDirective (
2525
el: ASTElement,
2626
name: string,
27+
rawName: string,
2728
value: string,
2829
arg: ?string,
2930
modifiers: ?{ [key: string]: true }
3031
) {
31-
(el.directives || (el.directives = [])).push({ name, value, arg, modifiers })
32+
(el.directives || (el.directives = [])).push({ name, rawName, value, arg, modifiers })
3233
}
3334

3435
export function addHandler (

src/compiler/parser/index.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -353,9 +353,9 @@ function processComponent (el) {
353353

354354
function processAttrs (el) {
355355
const list = el.attrsList
356-
let i, l, name, value, arg, modifiers, isProp
356+
let i, l, name, rawName, value, arg, modifiers, isProp
357357
for (i = 0, l = list.length; i < l; i++) {
358-
name = list[i].name
358+
name = rawName = list[i].name
359359
value = list[i].value
360360
if (dirRE.test(name)) {
361361
// mark element as dynamic
@@ -387,7 +387,7 @@ function processAttrs (el) {
387387
if (argMatch && (arg = argMatch[1])) {
388388
name = name.slice(0, -(arg.length + 1))
389389
}
390-
addDirective(el, name, value, arg, modifiers)
390+
addDirective(el, name, rawName, value, arg, modifiers)
391391
if (process.env.NODE_ENV !== 'production' && name === 'model') {
392392
checkForAliasModel(el, value)
393393
}

src/core/vdom/modules/directives.js

Lines changed: 93 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,76 +2,115 @@
22

33
import { resolveAsset } from 'core/util/options'
44
import { mergeVNodeHook } from 'core/vdom/helpers'
5+
import { emptyNode } from 'core/vdom/patch'
56

67
export default {
7-
create: function bindDirectives (oldVnode: VNodeWithData, vnode: VNodeWithData) {
8-
let hasInsert = false
9-
forEachDirective(oldVnode, vnode, (def, dir) => {
10-
callHook(def, dir, 'bind', vnode, oldVnode)
11-
if (def.inserted) {
12-
hasInsert = true
8+
create: updateDirectives,
9+
update: updateDirectives,
10+
destroy: function unbindDirectives (vnode: VNodeWithData) {
11+
updateDirectives(vnode, emptyNode)
12+
}
13+
}
14+
15+
function updateDirectives (
16+
oldVnode: VNodeWithData,
17+
vnode: VNodeWithData
18+
) {
19+
if (!oldVnode.data.directives && !vnode.data.directives) {
20+
return
21+
}
22+
const isCreate = oldVnode === emptyNode
23+
const oldDirs = normalizeDirectives(oldVnode.data.directives, oldVnode.context)
24+
const newDirs = normalizeDirectives(vnode.data.directives, vnode.context)
25+
26+
const dirsWithInsert = []
27+
const dirsWithPostpatch = []
28+
29+
let key, oldDir, dir
30+
for (key in newDirs) {
31+
oldDir = oldDirs[key]
32+
dir = newDirs[key]
33+
if (!oldDir) {
34+
// new directive, bind
35+
callHook(dir, 'bind', vnode, oldVnode)
36+
if (dir.def && dir.def.inserted) {
37+
dirsWithInsert.push(dir)
38+
}
39+
} else {
40+
// existing directive, update
41+
dir.oldValue = oldDir.value
42+
callHook(dir, 'update', vnode, oldVnode)
43+
if (dir.def && dir.def.componentUpdated) {
44+
dirsWithPostpatch.push(dir)
1345
}
14-
})
15-
if (hasInsert) {
16-
mergeVNodeHook(vnode.data.hook || (vnode.data.hook = {}), 'insert', () => {
17-
applyDirectives(oldVnode, vnode, 'inserted')
18-
}, 'dir-insert')
1946
}
20-
},
21-
update: function updateDirectives (oldVnode: VNodeWithData, vnode: VNodeWithData) {
22-
applyDirectives(oldVnode, vnode, 'update')
23-
// if old vnode has directives but new vnode doesn't
24-
// we need to teardown the directives on the old one.
25-
if (oldVnode.data.directives && !vnode.data.directives) {
26-
applyDirectives(oldVnode, oldVnode, 'unbind')
47+
}
48+
49+
if (dirsWithInsert.length) {
50+
const callInsert = () => {
51+
dirsWithInsert.forEach(dir => {
52+
callHook(dir, 'inserted', vnode, oldVnode)
53+
})
54+
}
55+
if (isCreate) {
56+
mergeVNodeHook(vnode.data.hook || (vnode.data.hook = {}), 'insert', callInsert, 'dir-insert')
57+
} else {
58+
callInsert()
59+
}
60+
}
61+
62+
if (dirsWithPostpatch.length) {
63+
mergeVNodeHook(vnode.data.hook || (vnode.data.hook = {}), 'postpatch', () => {
64+
dirsWithPostpatch.forEach(dir => {
65+
callHook(dir, 'componentUpdated', vnode, oldVnode)
66+
})
67+
}, 'dir-postpatch')
68+
}
69+
70+
if (!isCreate) {
71+
for (key in oldDirs) {
72+
if (!newDirs[key]) {
73+
// no longer present, unbind
74+
callHook(oldDirs[key], 'unbind', oldVnode)
75+
}
2776
}
28-
},
29-
postpatch: function postupdateDirectives (oldVnode: VNodeWithData, vnode: VNodeWithData) {
30-
applyDirectives(oldVnode, vnode, 'componentUpdated')
31-
},
32-
destroy: function unbindDirectives (vnode: VNodeWithData) {
33-
applyDirectives(vnode, vnode, 'unbind')
3477
}
3578
}
3679

3780
const emptyModifiers = Object.create(null)
3881

39-
function forEachDirective (
40-
oldVnode: VNodeWithData,
41-
vnode: VNodeWithData,
42-
fn: Function
43-
) {
44-
const dirs = vnode.data.directives
45-
if (dirs) {
46-
for (let i = 0; i < dirs.length; i++) {
47-
const dir = dirs[i]
48-
const def = resolveAsset(vnode.context.$options, 'directives', dir.name, true)
49-
if (def) {
50-
const oldDirs = oldVnode && oldVnode.data.directives
51-
if (oldDirs) {
52-
dir.oldValue = oldDirs[i].value
53-
}
54-
if (!dir.modifiers) {
55-
dir.modifiers = emptyModifiers
56-
}
57-
fn(def, dir)
58-
}
82+
function normalizeDirectives (
83+
dirs: ?Array<VNodeDirective>,
84+
vm: Component
85+
): { [key: string]: VNodeDirective } {
86+
const res = Object.create(null)
87+
if (!dirs) {
88+
return res
89+
}
90+
let i, dir
91+
for (i = 0; i < dirs.length; i++) {
92+
dir = dirs[i]
93+
res[getRawDirName(dir)] = dir
94+
if (!dir.modifiers) {
95+
dir.modifiers = emptyModifiers
5996
}
97+
dir.def = resolveAsset(vm.$options, 'directives', dir.name, true)
6098
}
99+
return res
61100
}
62101

63-
function applyDirectives (
64-
oldVnode: VNodeWithData,
65-
vnode: VNodeWithData,
66-
hook: string
67-
) {
68-
forEachDirective(oldVnode, vnode, (def, dir) => {
69-
callHook(def, dir, hook, vnode, oldVnode)
70-
})
102+
function getRawDirName (dir: VNodeDirective): string {
103+
return dir.rawName || (
104+
dir.name + (
105+
dir.modifiers
106+
? '.' + Object.keys(dir.modifiers).join('.')
107+
: ''
108+
)
109+
)
71110
}
72111

73-
function callHook (def, dir, hook, vnode, oldVnode) {
74-
const fn = def && def[hook]
112+
function callHook (dir, hook, vnode, oldVnode) {
113+
const fn = dir.def && dir.def[hook]
75114
if (fn) {
76115
fn(vnode.elm, dir, vnode, oldVnode)
77116
}

src/core/vdom/patch.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ import { isPrimitive, _toString, warn } from '../util/index'
1818
import { activeInstance } from '../instance/lifecycle'
1919
import { registerRef } from './modules/ref'
2020

21-
const emptyData = {}
22-
const emptyNode = new VNode('', emptyData, [])
23-
const hooks = ['create', 'update', 'postpatch', 'remove', 'destroy']
21+
export const emptyNode = new VNode('', {}, [])
22+
23+
const hooks = ['create', 'update', 'remove', 'destroy']
2424

2525
function isUndef (s) {
2626
return s == null
@@ -344,17 +344,18 @@ export function createPatchFunction (backend) {
344344
vnode.elm = oldVnode.elm
345345
return
346346
}
347-
let i, hook
348-
const hasData = isDef(i = vnode.data)
349-
if (hasData && isDef(hook = i.hook) && isDef(i = hook.prepatch)) {
347+
let i
348+
const data = vnode.data
349+
const hasData = isDef(data)
350+
if (hasData && isDef(i = data.hook) && isDef(i = i.prepatch)) {
350351
i(oldVnode, vnode)
351352
}
352353
const elm = vnode.elm = oldVnode.elm
353354
const oldCh = oldVnode.children
354355
const ch = vnode.children
355356
if (hasData && isPatchable(vnode)) {
356357
for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode)
357-
if (isDef(hook) && isDef(i = hook.update)) i(oldVnode, vnode)
358+
if (isDef(i = data.hook) && isDef(i = i.update)) i(oldVnode, vnode)
358359
}
359360
if (isUndef(vnode.text)) {
360361
if (isDef(oldCh) && isDef(ch)) {
@@ -371,8 +372,7 @@ export function createPatchFunction (backend) {
371372
nodeOps.setTextContent(elm, vnode.text)
372373
}
373374
if (hasData) {
374-
for (i = 0; i < cbs.postpatch.length; ++i) cbs.postpatch[i](oldVnode, vnode)
375-
if (isDef(hook) && isDef(i = hook.postpatch)) i(oldVnode, vnode)
375+
if (isDef(i = data.hook) && isDef(i = i.postpatch)) i(oldVnode, vnode)
376376
}
377377
}
378378

test/unit/modules/compiler/codegen.spec.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ function assertCodegen (template, generatedCode, ...args) {
3232
describe('codegen', () => {
3333
it('generate directive', () => {
3434
assertCodegen(
35-
'<p v-custom1:arg1.modifire="value1" v-custom2><p>',
36-
`with(this){return _h('p',{directives:[{name:"custom1",value:(value1),expression:"value1",arg:"arg1",modifiers:{"modifire":true}},{name:"custom2",arg:"arg1"}]})}`
35+
'<p v-custom1:arg1.modifier="value1" v-custom2><p>',
36+
`with(this){return _h('p',{directives:[{name:"custom1",rawName:"v-custom1:arg1.modifier",value:(value1),expression:"value1",arg:"arg1",modifiers:{"modifier":true}},{name:"custom2",rawName:"v-custom2",arg:"arg1"}]})}`
3737
)
3838
})
3939

@@ -148,7 +148,7 @@ describe('codegen', () => {
148148
it('generate v-show directive', () => {
149149
assertCodegen(
150150
'<p v-show="shown">hello world</p>',
151-
`with(this){return _h('p',{directives:[{name:"show",value:(shown),expression:"shown"}]},["hello world"])}`
151+
`with(this){return _h('p',{directives:[{name:"show",rawName:"v-show",value:(shown),expression:"shown"}]},["hello world"])}`
152152
)
153153
})
154154

0 commit comments

Comments
 (0)