Skip to content

Commit 6cbee6b

Browse files
committed
refactor: extract universal v-model codegen code and update weex v-model codegen
1 parent 90a455c commit 6cbee6b

File tree

6 files changed

+146
-152
lines changed

6 files changed

+146
-152
lines changed

Diff for: src/compiler/directives/model.js

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/* @flow */
2+
3+
/**
4+
* Cross-platform code generation for component v-model
5+
*/
6+
export function genComponentModel (
7+
el: ASTElement,
8+
value: string,
9+
modifiers: ?ASTModifiers
10+
): ?boolean {
11+
const { number, trim } = modifiers || {}
12+
13+
let valueExpression = 'value'
14+
if (trim) {
15+
valueExpression = `(typeof value === 'string' ? value.trim() : value)`
16+
}
17+
if (number) {
18+
valueExpression = `_n(${valueExpression})`
19+
}
20+
21+
el.model = {
22+
value: `(${value})`,
23+
callback: `function (value) {${genAssignmentCode(value, valueExpression)}}`
24+
}
25+
}
26+
27+
/**
28+
* Cross-platform codegen helper for generating v-model value assignment code.
29+
*/
30+
export function genAssignmentCode (
31+
value: string,
32+
assignment: string
33+
): string {
34+
const modelRs = parseModel(value)
35+
if (modelRs.idx === null) {
36+
return `${value}=${assignment}`
37+
} else {
38+
return `var $$exp = ${modelRs.exp}, $$idx = ${modelRs.idx};` +
39+
`if (!Array.isArray($$exp)){` +
40+
`${value}=${assignment}}` +
41+
`else{$$exp.splice($$idx, 1, ${assignment})}`
42+
}
43+
}
44+
45+
/**
46+
* parse directive model to do the array update transform. a[idx] = val => $$a.splice($$idx, 1, val)
47+
*
48+
* for loop possible cases:
49+
*
50+
* - test
51+
* - test[idx]
52+
* - test[test1[idx]]
53+
* - test["a"][idx]
54+
* - xxx.test[a[a].test1[idx]]
55+
* - test.xxx.a["asa"][test1[idx]]
56+
*
57+
*/
58+
59+
let len, str, chr, index, expressionPos, expressionEndPos
60+
61+
export function parseModel (val: string): Object {
62+
str = val
63+
len = str.length
64+
index = expressionPos = expressionEndPos = 0
65+
66+
if (val.indexOf('[') < 0 || val.lastIndexOf(']') < len - 1) {
67+
return {
68+
exp: val,
69+
idx: null
70+
}
71+
}
72+
73+
while (!eof()) {
74+
chr = next()
75+
/* istanbul ignore if */
76+
if (isStringStart(chr)) {
77+
parseString(chr)
78+
} else if (chr === 0x5B) {
79+
parseBracket(chr)
80+
}
81+
}
82+
83+
return {
84+
exp: val.substring(0, expressionPos),
85+
idx: val.substring(expressionPos + 1, expressionEndPos)
86+
}
87+
}
88+
89+
function next (): number {
90+
return str.charCodeAt(++index)
91+
}
92+
93+
function eof (): boolean {
94+
return index >= len
95+
}
96+
97+
function isStringStart (chr: number): boolean {
98+
return chr === 0x22 || chr === 0x27
99+
}
100+
101+
function parseBracket (chr: number): void {
102+
let inBracket = 1
103+
expressionPos = index
104+
while (!eof()) {
105+
chr = next()
106+
if (isStringStart(chr)) {
107+
parseString(chr)
108+
continue
109+
}
110+
if (chr === 0x5B) inBracket++
111+
if (chr === 0x5D) inBracket--
112+
if (inBracket === 0) {
113+
expressionEndPos = index
114+
break
115+
}
116+
}
117+
}
118+
119+
function parseString (chr: number): void {
120+
const stringQuote = chr
121+
while (!eof()) {
122+
chr = next()
123+
if (chr === stringQuote) {
124+
break
125+
}
126+
}
127+
}

Diff for: src/compiler/helpers.js

-84
Original file line numberDiff line numberDiff line change
@@ -100,87 +100,3 @@ export function getAndRemoveAttr (el: ASTElement, name: string): ?string {
100100
}
101101
return val
102102
}
103-
104-
let len, str, chr, index, expressionPos, expressionEndPos
105-
106-
/**
107-
* parse directive model to do the array update transform. a[idx] = val => $$a.splice($$idx, 1, val)
108-
*
109-
* for loop possible cases:
110-
*
111-
* - test
112-
* - test[idx]
113-
* - test[test1[idx]]
114-
* - test["a"][idx]
115-
* - xxx.test[a[a].test1[idx]]
116-
* - test.xxx.a["asa"][test1[idx]]
117-
*
118-
*/
119-
120-
export function parseModel (val: string): Object {
121-
str = val
122-
len = str.length
123-
index = expressionPos = expressionEndPos = 0
124-
125-
if (val.indexOf('[') < 0 || val.lastIndexOf(']') < len - 1) {
126-
return {
127-
exp: val,
128-
idx: null
129-
}
130-
}
131-
132-
while (!eof()) {
133-
chr = next()
134-
/* istanbul ignore if */
135-
if (isStringStart(chr)) {
136-
parseString(chr)
137-
} else if (chr === 0x5B) {
138-
parseBracket(chr)
139-
}
140-
}
141-
142-
return {
143-
exp: val.substring(0, expressionPos),
144-
idx: val.substring(expressionPos + 1, expressionEndPos)
145-
}
146-
}
147-
148-
function next (): number {
149-
return str.charCodeAt(++index)
150-
}
151-
152-
function eof (): boolean {
153-
return index >= len
154-
}
155-
156-
function isStringStart (chr: number): boolean {
157-
return chr === 0x22 || chr === 0x27
158-
}
159-
160-
function parseBracket (chr: number): void {
161-
let inBracket = 1
162-
expressionPos = index
163-
while (!eof()) {
164-
chr = next()
165-
if (isStringStart(chr)) {
166-
parseString(chr)
167-
continue
168-
}
169-
if (chr === 0x5B) inBracket++
170-
if (chr === 0x5D) inBracket--
171-
if (inBracket === 0) {
172-
expressionEndPos = index
173-
break
174-
}
175-
}
176-
}
177-
178-
function parseString (chr: number): void {
179-
const stringQuote = chr
180-
while (!eof()) {
181-
chr = next()
182-
if (chr === stringQuote) {
183-
break
184-
}
185-
}
186-
}

Diff for: src/platforms/web/compiler/directives/model.js

+2-34
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
import config from 'core/config'
44
import { isIE } from 'core/util/env'
5-
import { addHandler, addProp, getBindingAttr, parseModel } from 'compiler/helpers'
5+
import { addHandler, addProp, getBindingAttr } from 'compiler/helpers'
6+
import { genComponentModel, genAssignmentCode } from 'compiler/directives/model'
67

78
let warn
89

@@ -183,36 +184,3 @@ function genDefaultModel (
183184
addHandler(el, 'blur', '$forceUpdate()')
184185
}
185186
}
186-
187-
function genComponentModel (
188-
el: ASTElement,
189-
value: string,
190-
modifiers: ?ASTModifiers
191-
): ?boolean {
192-
const { number, trim } = modifiers || {}
193-
194-
let valueExpression = 'value'
195-
if (trim) {
196-
valueExpression = `(typeof value === 'string' ? value.trim() : value)`
197-
}
198-
if (number) {
199-
valueExpression = `_n(${valueExpression})`
200-
}
201-
202-
el.model = {
203-
value,
204-
callback: `function (value) {${genAssignmentCode(value, valueExpression)}}`
205-
}
206-
}
207-
208-
function genAssignmentCode (value: string, assignment: string): string {
209-
const modelRs = parseModel(value)
210-
if (modelRs.idx === null) {
211-
return `${value}=${assignment}`
212-
} else {
213-
return `var $$exp = ${modelRs.exp}, $$idx = ${modelRs.idx};` +
214-
`if (!Array.isArray($$exp)){` +
215-
`${value}=${assignment}}` +
216-
`else{$$exp.splice($$idx, 1, ${assignment})}`
217-
}
218-
}

Diff for: src/platforms/weex/compiler/directives/model.js

+14-19
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,34 @@
11
/* @flow */
22

3-
import { addHandler, addAttr, parseModel } from 'compiler/helpers'
3+
import { addHandler, addAttr } from 'compiler/helpers'
4+
import { genComponentModel, genAssignmentCode } from 'compiler/directives/model'
45

56
export default function model (
67
el: ASTElement,
78
dir: ASTDirective,
89
_warn: Function
910
): ?boolean {
10-
genDefaultModel(el, dir.value, dir.modifiers)
11+
if (el.tag === 'input' || el.tag === 'textarea') {
12+
genDefaultModel(el, dir.value, dir.modifiers)
13+
} else {
14+
genComponentModel(el, dir.value, dir.modifiers)
15+
}
1116
}
1217

1318
function genDefaultModel (
1419
el: ASTElement,
1520
value: string,
1621
modifiers: ?ASTModifiers
1722
): ?boolean {
18-
const { lazy, trim } = modifiers || {}
23+
const { lazy, trim, number } = modifiers || {}
1924
const event = lazy ? 'change' : 'input'
20-
const isNative = el.tag === 'input' || el.tag === 'textarea'
21-
const valueExpression = isNative
22-
? `$event.target.attr.value${trim ? '.trim()' : ''}`
23-
: `$event`
25+
26+
let valueExpression = `$event.target.attr.value${trim ? '.trim()' : ''}`
27+
if (number) {
28+
valueExpression = `_n(${valueExpression})`
29+
}
30+
2431
const code = genAssignmentCode(value, valueExpression)
2532
addAttr(el, 'value', `(${value})`)
2633
addHandler(el, event, code, null, true)
2734
}
28-
29-
function genAssignmentCode (value: string, assignment: string): string {
30-
const modelRs = parseModel(value)
31-
if (modelRs.idx === null) {
32-
return `${value}=${assignment}`
33-
} else {
34-
return `var $$exp = ${modelRs.exp}, $$idx = ${modelRs.idx};` +
35-
`if (!Array.isArray($$exp)){` +
36-
`${value}=${assignment}}` +
37-
`else{$$exp.splice($$idx, 1, ${assignment})}`
38-
}
39-
}

Diff for: test/unit/features/directives/model-parse.spec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { parseModel } from 'compiler/helpers'
1+
import { parseModel } from 'compiler/directives/model'
22

33
describe('model expression parser', () => {
44
it('parse object dot notation', () => {

Diff for: test/weex/compiler/v-model.spec.js

+2-14
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,16 @@ describe('compile v-model', () => {
1414
it('should compile other component with whole $event as the value', () => {
1515
const { render, staticRenderFns, errors } = compile(`<div><foo v-model="x" /></div>`)
1616
expect(render).not.toBeUndefined()
17-
expect(render).toMatch(strToRegExp(`attrs:{"value":(x)}`))
18-
expect(render).toMatch(strToRegExp(`on:{"input":function($event){x=$event}}`))
19-
expect(staticRenderFns).toEqual([])
20-
expect(errors).toEqual([])
21-
})
22-
23-
it('should compile with lazy modifier', () => {
24-
const { render, staticRenderFns, errors } = compile(`<div><foo v-model.lazy="x" /></div>`)
25-
expect(render).not.toBeUndefined()
26-
expect(render).toMatch(strToRegExp(`attrs:{"value":(x)}`))
27-
expect(render).toMatch(strToRegExp(`on:{"change":function($event){x=$event}}`))
17+
expect(render).toMatch(strToRegExp(`model:{value:(x),callback:function (value) {x=value}}`))
2818
expect(staticRenderFns).toEqual([])
2919
expect(errors).toEqual([])
3020
})
3121

3222
it('should compile with trim modifier for modelable native component', () => {
33-
const { render, staticRenderFns, errors } = compile(`<div><input v-model.trim="x" /><foo v-model.trim="y" /></div>`)
23+
const { render, staticRenderFns, errors } = compile(`<div><input v-model.trim="x" /></div>`)
3424
expect(render).not.toBeUndefined()
3525
expect(render).toMatch(strToRegExp(`attrs:{"value":(x)}`))
36-
expect(render).toMatch(strToRegExp(`attrs:{"value":(y)}`))
3726
expect(render).toMatch(strToRegExp(`on:{"input":function($event){x=$event.target.attr.value.trim()}}`))
38-
expect(render).toMatch(strToRegExp(`on:{"input":function($event){y=$event}}`))
3927
expect(staticRenderFns).toEqual([])
4028
expect(errors).toEqual([])
4129
})

0 commit comments

Comments
 (0)