Skip to content

Commit

Permalink
feat: dynamic directive arguments for v-on, v-bind and custom directi…
Browse files Browse the repository at this point in the history
…ves (#9373)
  • Loading branch information
yyx990803 committed Jan 26, 2019
1 parent f219bed commit dbc0582
Show file tree
Hide file tree
Showing 15 changed files with 459 additions and 68 deletions.
12 changes: 11 additions & 1 deletion flow/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,20 @@ declare type ModuleOptions = {
declare type ASTModifiers = { [key: string]: boolean };
declare type ASTIfCondition = { exp: ?string; block: ASTElement };
declare type ASTIfConditions = Array<ASTIfCondition>;
declare type ASTAttr = { name: string; value: any; start?: number; end?: number };

declare type ASTAttr = {
name: string;
value: any;
dynamic?: boolean;
start?: number;
end?: number
};

declare type ASTElementHandler = {
value: string;
params?: Array<any>;
modifiers: ?ASTModifiers;
dynamic?: boolean;
start?: number;
end?: number;
};
Expand All @@ -80,6 +88,7 @@ declare type ASTDirective = {
rawName: string;
value: string;
arg: ?string;
isDynamicArg: boolean;
modifiers: ?ASTModifiers;
start?: number;
end?: number;
Expand Down Expand Up @@ -109,6 +118,7 @@ declare type ASTElement = {

text?: string;
attrs?: Array<ASTAttr>;
dynamicAttrs?: Array<ASTAttr>;
props?: Array<ASTAttr>;
plain?: boolean;
pre?: true;
Expand Down
1 change: 1 addition & 0 deletions flow/vnode.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ declare type VNodeDirective = {
value?: any;
oldValue?: any;
arg?: string;
oldArg?: string;
modifiers?: ASTModifiers;
def?: Object;
};
Expand Down
27 changes: 18 additions & 9 deletions src/compiler/codegen/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,23 @@ export function genHandlers (
events: ASTElementHandlers,
isNative: boolean
): string {
let res = isNative ? 'nativeOn:{' : 'on:{'
const prefix = isNative ? 'nativeOn:' : 'on:'
let staticHandlers = ``
let dynamicHandlers = ``
for (const name in events) {
res += `"${name}":${genHandler(name, events[name])},`
const handlerCode = genHandler(events[name])
if (events[name] && events[name].dynamic) {
dynamicHandlers += `${name},${handlerCode},`
} else {
staticHandlers += `"${name}":${handlerCode},`
}
}
staticHandlers = `{${staticHandlers.slice(0, -1)}}`
if (dynamicHandlers) {
return prefix + `_d(${staticHandlers},[${dynamicHandlers.slice(0, -1)}])`
} else {
return prefix + staticHandlers
}
return res.slice(0, -1) + '}'
}

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

function genHandler (
name: string,
handler: ASTElementHandler | Array<ASTElementHandler>
): string {
function genHandler (handler: ASTElementHandler | Array<ASTElementHandler>): string {
if (!handler) {
return 'function(){}'
}

if (Array.isArray(handler)) {
return `[${handler.map(handler => genHandler(name, handler)).join(',')}]`
return `[${handler.map(handler => genHandler(handler)).join(',')}]`
}

const isMethodPath = simplePathRE.test(handler.value)
Expand Down Expand Up @@ -154,7 +163,7 @@ function genHandler (
}

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

function genFilterCode (key: string): string {
Expand Down
32 changes: 23 additions & 9 deletions src/compiler/codegen/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,11 +248,11 @@ export function genData (el: ASTElement, state: CodegenState): string {
}
// attributes
if (el.attrs) {
data += `attrs:{${genProps(el.attrs)}},`
data += `attrs:${genProps(el.attrs)},`
}
// DOM props
if (el.props) {
data += `domProps:{${genProps(el.props)}},`
data += `domProps:${genProps(el.props)},`
}
// event handlers
if (el.events) {
Expand Down Expand Up @@ -288,6 +288,12 @@ export function genData (el: ASTElement, state: CodegenState): string {
}
}
data = data.replace(/,$/, '') + '}'
// v-bind dynamic argument wrap
// v-bind with dynamic arguments must be applied using the same v-bind object
// merge helper so that class/style/mustUseProp attrs are handled correctly.
if (el.dynamicAttrs) {
data = `_b(${data},"${el.tag}",${genProps(el.dynamicAttrs)})`
}
// v-bind data wrap
if (el.wrapData) {
data = el.wrapData(data)
Expand Down Expand Up @@ -319,7 +325,7 @@ function genDirectives (el: ASTElement, state: CodegenState): string | void {
res += `{name:"${dir.name}",rawName:"${dir.rawName}"${
dir.value ? `,value:(${dir.value}),expression:${JSON.stringify(dir.value)}` : ''
}${
dir.arg ? `,arg:"${dir.arg}"` : ''
dir.arg ? `,arg:${dir.isDynamicArg ? dir.arg : `"${dir.arg}"`}` : ''
}${
dir.modifiers ? `,modifiers:${JSON.stringify(dir.modifiers)}` : ''
}},`
Expand Down Expand Up @@ -510,17 +516,25 @@ function genComponent (
}

function genProps (props: Array<ASTAttr>): string {
let res = ''
let staticProps = ``
let dynamicProps = ``
for (let i = 0; i < props.length; i++) {
const prop = props[i]
/* istanbul ignore if */
if (__WEEX__) {
res += `"${prop.name}":${generateValue(prop.value)},`
const value = __WEEX__
? generateValue(prop.value)
: transformSpecialNewlines(prop.value)
if (prop.dynamic) {
dynamicProps += `${prop.name},${value},`
} else {
res += `"${prop.name}":${transformSpecialNewlines(prop.value)},`
staticProps += `"${prop.name}":${value},`
}
}
return res.slice(0, -1)
staticProps = `{${staticProps.slice(0, -1)}}`
if (dynamicProps) {
return `_d(${staticProps},[${dynamicProps.slice(0, -1)}])`
} else {
return staticProps
}
}

/* istanbul ignore next */
Expand Down
50 changes: 37 additions & 13 deletions src/compiler/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@ export function pluckModuleFunction<F: Function> (
: []
}

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

export function addAttr (el: ASTElement, name: string, value: any, range?: Range) {
(el.attrs || (el.attrs = [])).push(rangeSetItem({ name, value }, range))
export function addAttr (el: ASTElement, name: string, value: any, range?: Range, dynamic?: boolean) {
const attrs = dynamic
? (el.dynamicAttrs || (el.dynamicAttrs = []))
: (el.attrs || (el.attrs = []))
attrs.push(rangeSetItem({ name, value, dynamic }, range))
el.plain = false
}

Expand All @@ -42,21 +45,36 @@ export function addDirective (
rawName: string,
value: string,
arg: ?string,
isDynamicArg: boolean,
modifiers: ?ASTModifiers,
range?: Range
) {
(el.directives || (el.directives = [])).push(rangeSetItem({ name, rawName, value, arg, modifiers }, range))
(el.directives || (el.directives = [])).push(rangeSetItem({
name,
rawName,
value,
arg,
isDynamicArg,
modifiers
}, range))
el.plain = false
}

function prependModifierMarker (symbol: string, name: string, dynamic?: boolean): string {
return dynamic
? `_p(${name},"${symbol}")`
: symbol + name // mark the event as captured
}

export function addHandler (
el: ASTElement,
name: string,
value: string,
modifiers: ?ASTModifiers,
important?: boolean,
warn?: ?Function,
range?: Range
range?: Range,
dynamic?: boolean
) {
modifiers = modifiers || emptyObject
// warn prevent and passive modifier
Expand All @@ -75,28 +93,34 @@ export function addHandler (
// normalize click.right and click.middle since they don't actually fire
// this is technically browser-specific, but at least for now browsers are
// the only target envs that have right/middle clicks.
if (name === 'click') {
if (modifiers.right) {
if (modifiers.right) {
if (dynamic) {
name = `(${name})==='click'?'contextmenu':(${name})`
} else if (name === 'click') {
name = 'contextmenu'
delete modifiers.right
} else if (modifiers.middle) {
}
} else if (modifiers.middle) {
if (dynamic) {
name = `(${name})==='click'?'mouseup':(${name})`
} else if (name === 'click') {
name = 'mouseup'
}
}

// check capture modifier
if (modifiers.capture) {
delete modifiers.capture
name = '!' + name // mark the event as captured
name = prependModifierMarker('!', name, dynamic)
}
if (modifiers.once) {
delete modifiers.once
name = '~' + name // mark the event as once
name = prependModifierMarker('~', name, dynamic)
}
/* istanbul ignore if */
if (modifiers.passive) {
delete modifiers.passive
name = '&' + name // mark the event as passive
name = prependModifierMarker('&', name, dynamic)
}

let events
Expand All @@ -107,7 +131,7 @@ export function addHandler (
events = el.events || (el.events = {})
}

const newHandler: any = rangeSetItem({ value: value.trim() }, range)
const newHandler: any = rangeSetItem({ value: value.trim(), dynamic }, range)
if (modifiers !== emptyObject) {
newHandler.modifiers = modifiers
}
Expand Down
Loading

0 comments on commit dbc0582

Please sign in to comment.