Skip to content

Commit dbc0582

Browse files
committed
feat: dynamic directive arguments for v-on, v-bind and custom directives (#9373)
1 parent f219bed commit dbc0582

File tree

15 files changed

+459
-68
lines changed

15 files changed

+459
-68
lines changed

flow/compiler.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,20 @@ declare type ModuleOptions = {
6161
declare type ASTModifiers = { [key: string]: boolean };
6262
declare type ASTIfCondition = { exp: ?string; block: ASTElement };
6363
declare type ASTIfConditions = Array<ASTIfCondition>;
64-
declare type ASTAttr = { name: string; value: any; start?: number; end?: number };
64+
65+
declare type ASTAttr = {
66+
name: string;
67+
value: any;
68+
dynamic?: boolean;
69+
start?: number;
70+
end?: number
71+
};
6572

6673
declare type ASTElementHandler = {
6774
value: string;
6875
params?: Array<any>;
6976
modifiers: ?ASTModifiers;
77+
dynamic?: boolean;
7078
start?: number;
7179
end?: number;
7280
};
@@ -80,6 +88,7 @@ declare type ASTDirective = {
8088
rawName: string;
8189
value: string;
8290
arg: ?string;
91+
isDynamicArg: boolean;
8392
modifiers: ?ASTModifiers;
8493
start?: number;
8594
end?: number;
@@ -109,6 +118,7 @@ declare type ASTElement = {
109118

110119
text?: string;
111120
attrs?: Array<ASTAttr>;
121+
dynamicAttrs?: Array<ASTAttr>;
112122
props?: Array<ASTAttr>;
113123
plain?: boolean;
114124
pre?: true;

flow/vnode.js

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ declare type VNodeDirective = {
7171
value?: any;
7272
oldValue?: any;
7373
arg?: string;
74+
oldArg?: string;
7475
modifiers?: ASTModifiers;
7576
def?: Object;
7677
};

src/compiler/codegen/events.js

+18-9
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,23 @@ export function genHandlers (
5656
events: ASTElementHandlers,
5757
isNative: boolean
5858
): string {
59-
let res = isNative ? 'nativeOn:{' : 'on:{'
59+
const prefix = isNative ? 'nativeOn:' : 'on:'
60+
let staticHandlers = ``
61+
let dynamicHandlers = ``
6062
for (const name in events) {
61-
res += `"${name}":${genHandler(name, events[name])},`
63+
const handlerCode = genHandler(events[name])
64+
if (events[name] && events[name].dynamic) {
65+
dynamicHandlers += `${name},${handlerCode},`
66+
} else {
67+
staticHandlers += `"${name}":${handlerCode},`
68+
}
69+
}
70+
staticHandlers = `{${staticHandlers.slice(0, -1)}}`
71+
if (dynamicHandlers) {
72+
return prefix + `_d(${staticHandlers},[${dynamicHandlers.slice(0, -1)}])`
73+
} else {
74+
return prefix + staticHandlers
6275
}
63-
return res.slice(0, -1) + '}'
6476
}
6577

6678
// Generate handler code with binding params on Weex
@@ -81,16 +93,13 @@ function genWeexHandler (params: Array<any>, handlerCode: string) {
8193
'}'
8294
}
8395

84-
function genHandler (
85-
name: string,
86-
handler: ASTElementHandler | Array<ASTElementHandler>
87-
): string {
96+
function genHandler (handler: ASTElementHandler | Array<ASTElementHandler>): string {
8897
if (!handler) {
8998
return 'function(){}'
9099
}
91100

92101
if (Array.isArray(handler)) {
93-
return `[${handler.map(handler => genHandler(name, handler)).join(',')}]`
102+
return `[${handler.map(handler => genHandler(handler)).join(',')}]`
94103
}
95104

96105
const isMethodPath = simplePathRE.test(handler.value)
@@ -154,7 +163,7 @@ function genHandler (
154163
}
155164

156165
function genKeyFilter (keys: Array<string>): string {
157-
return `if(!('button' in $event)&&${keys.map(genFilterCode).join('&&')})return null;`
166+
return `if(('keyCode' in $event)&&${keys.map(genFilterCode).join('&&')})return null;`
158167
}
159168

160169
function genFilterCode (key: string): string {

src/compiler/codegen/index.js

+23-9
Original file line numberDiff line numberDiff line change
@@ -248,11 +248,11 @@ export function genData (el: ASTElement, state: CodegenState): string {
248248
}
249249
// attributes
250250
if (el.attrs) {
251-
data += `attrs:{${genProps(el.attrs)}},`
251+
data += `attrs:${genProps(el.attrs)},`
252252
}
253253
// DOM props
254254
if (el.props) {
255-
data += `domProps:{${genProps(el.props)}},`
255+
data += `domProps:${genProps(el.props)},`
256256
}
257257
// event handlers
258258
if (el.events) {
@@ -288,6 +288,12 @@ export function genData (el: ASTElement, state: CodegenState): string {
288288
}
289289
}
290290
data = data.replace(/,$/, '') + '}'
291+
// v-bind dynamic argument wrap
292+
// v-bind with dynamic arguments must be applied using the same v-bind object
293+
// merge helper so that class/style/mustUseProp attrs are handled correctly.
294+
if (el.dynamicAttrs) {
295+
data = `_b(${data},"${el.tag}",${genProps(el.dynamicAttrs)})`
296+
}
291297
// v-bind data wrap
292298
if (el.wrapData) {
293299
data = el.wrapData(data)
@@ -319,7 +325,7 @@ function genDirectives (el: ASTElement, state: CodegenState): string | void {
319325
res += `{name:"${dir.name}",rawName:"${dir.rawName}"${
320326
dir.value ? `,value:(${dir.value}),expression:${JSON.stringify(dir.value)}` : ''
321327
}${
322-
dir.arg ? `,arg:"${dir.arg}"` : ''
328+
dir.arg ? `,arg:${dir.isDynamicArg ? dir.arg : `"${dir.arg}"`}` : ''
323329
}${
324330
dir.modifiers ? `,modifiers:${JSON.stringify(dir.modifiers)}` : ''
325331
}},`
@@ -510,17 +516,25 @@ function genComponent (
510516
}
511517

512518
function genProps (props: Array<ASTAttr>): string {
513-
let res = ''
519+
let staticProps = ``
520+
let dynamicProps = ``
514521
for (let i = 0; i < props.length; i++) {
515522
const prop = props[i]
516-
/* istanbul ignore if */
517-
if (__WEEX__) {
518-
res += `"${prop.name}":${generateValue(prop.value)},`
523+
const value = __WEEX__
524+
? generateValue(prop.value)
525+
: transformSpecialNewlines(prop.value)
526+
if (prop.dynamic) {
527+
dynamicProps += `${prop.name},${value},`
519528
} else {
520-
res += `"${prop.name}":${transformSpecialNewlines(prop.value)},`
529+
staticProps += `"${prop.name}":${value},`
521530
}
522531
}
523-
return res.slice(0, -1)
532+
staticProps = `{${staticProps.slice(0, -1)}}`
533+
if (dynamicProps) {
534+
return `_d(${staticProps},[${dynamicProps.slice(0, -1)}])`
535+
} else {
536+
return staticProps
537+
}
524538
}
525539

526540
/* istanbul ignore next */

src/compiler/helpers.js

+37-13
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,16 @@ export function pluckModuleFunction<F: Function> (
2020
: []
2121
}
2222

23-
export function addProp (el: ASTElement, name: string, value: string, range?: Range) {
24-
(el.props || (el.props = [])).push(rangeSetItem({ name, value }, range))
23+
export function addProp (el: ASTElement, name: string, value: string, range?: Range, dynamic?: boolean) {
24+
(el.props || (el.props = [])).push(rangeSetItem({ name, value, dynamic }, range))
2525
el.plain = false
2626
}
2727

28-
export function addAttr (el: ASTElement, name: string, value: any, range?: Range) {
29-
(el.attrs || (el.attrs = [])).push(rangeSetItem({ name, value }, range))
28+
export function addAttr (el: ASTElement, name: string, value: any, range?: Range, dynamic?: boolean) {
29+
const attrs = dynamic
30+
? (el.dynamicAttrs || (el.dynamicAttrs = []))
31+
: (el.attrs || (el.attrs = []))
32+
attrs.push(rangeSetItem({ name, value, dynamic }, range))
3033
el.plain = false
3134
}
3235

@@ -42,21 +45,36 @@ export function addDirective (
4245
rawName: string,
4346
value: string,
4447
arg: ?string,
48+
isDynamicArg: boolean,
4549
modifiers: ?ASTModifiers,
4650
range?: Range
4751
) {
48-
(el.directives || (el.directives = [])).push(rangeSetItem({ name, rawName, value, arg, modifiers }, range))
52+
(el.directives || (el.directives = [])).push(rangeSetItem({
53+
name,
54+
rawName,
55+
value,
56+
arg,
57+
isDynamicArg,
58+
modifiers
59+
}, range))
4960
el.plain = false
5061
}
5162

63+
function prependModifierMarker (symbol: string, name: string, dynamic?: boolean): string {
64+
return dynamic
65+
? `_p(${name},"${symbol}")`
66+
: symbol + name // mark the event as captured
67+
}
68+
5269
export function addHandler (
5370
el: ASTElement,
5471
name: string,
5572
value: string,
5673
modifiers: ?ASTModifiers,
5774
important?: boolean,
5875
warn?: ?Function,
59-
range?: Range
76+
range?: Range,
77+
dynamic?: boolean
6078
) {
6179
modifiers = modifiers || emptyObject
6280
// warn prevent and passive modifier
@@ -75,28 +93,34 @@ export function addHandler (
7593
// normalize click.right and click.middle since they don't actually fire
7694
// this is technically browser-specific, but at least for now browsers are
7795
// the only target envs that have right/middle clicks.
78-
if (name === 'click') {
79-
if (modifiers.right) {
96+
if (modifiers.right) {
97+
if (dynamic) {
98+
name = `(${name})==='click'?'contextmenu':(${name})`
99+
} else if (name === 'click') {
80100
name = 'contextmenu'
81101
delete modifiers.right
82-
} else if (modifiers.middle) {
102+
}
103+
} else if (modifiers.middle) {
104+
if (dynamic) {
105+
name = `(${name})==='click'?'mouseup':(${name})`
106+
} else if (name === 'click') {
83107
name = 'mouseup'
84108
}
85109
}
86110

87111
// check capture modifier
88112
if (modifiers.capture) {
89113
delete modifiers.capture
90-
name = '!' + name // mark the event as captured
114+
name = prependModifierMarker('!', name, dynamic)
91115
}
92116
if (modifiers.once) {
93117
delete modifiers.once
94-
name = '~' + name // mark the event as once
118+
name = prependModifierMarker('~', name, dynamic)
95119
}
96120
/* istanbul ignore if */
97121
if (modifiers.passive) {
98122
delete modifiers.passive
99-
name = '&' + name // mark the event as passive
123+
name = prependModifierMarker('&', name, dynamic)
100124
}
101125

102126
let events
@@ -107,7 +131,7 @@ export function addHandler (
107131
events = el.events || (el.events = {})
108132
}
109133

110-
const newHandler: any = rangeSetItem({ value: value.trim() }, range)
134+
const newHandler: any = rangeSetItem({ value: value.trim(), dynamic }, range)
111135
if (modifiers !== emptyObject) {
112136
newHandler.modifiers = modifiers
113137
}

0 commit comments

Comments
 (0)