diff --git a/packages/uni-mp-compiler/__tests__/codegen.spec.ts b/packages/uni-mp-compiler/__tests__/codegen.spec.ts
index 2007e759b1543a0cdbb861b52b564a50a4e4c459..3f681d15487527a2fd611b6123fcf0d376550441 100644
--- a/packages/uni-mp-compiler/__tests__/codegen.spec.ts
+++ b/packages/uni-mp-compiler/__tests__/codegen.spec.ts
@@ -4,11 +4,11 @@ describe('compiler: codegen', () => {
test('module mode preamble', () => {
assert(
``,
- ``,
+ ``,
`import { vOn as _vOn, vFor as _vFor } from "vue"
export function render(_ctx, _cache) {
- return { a: _vFor(_ctx.items, item => { return { a: _vOn(_ctx.onClick) }; }) }
+ return { a: _vFor(_ctx.items, item => { return {}; }), b: _vOn(_ctx.onClick) }
}`,
{ inline: false, mode: 'module', prefixIdentifiers: false }
)
@@ -17,11 +17,11 @@ export function render(_ctx, _cache) {
test('module mode preamble w/ optimizeImports: true', () => {
assert(
``,
- ``,
+ ``,
`import { vOn as _vOn, vFor as _vFor } from "vue"
export function render(_ctx, _cache) {
- return { a: _vFor(_ctx.items, item => { return { a: _vOn(_ctx.onClick) }; }) }
+ return { a: _vFor(_ctx.items, item => { return {}; }), b: _vOn(_ctx.onClick) }
}`,
{ inline: false, mode: 'module' }
)
@@ -30,14 +30,14 @@ export function render(_ctx, _cache) {
test('function mode preamble', () => {
assert(
``,
- ``,
+ ``,
`const _Vue = Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { vOn: _vOn, vFor: _vFor } = _Vue
- return { a: _vFor(items, item => { return { a: _vOn(onClick) }; }) }
+ return { a: _vFor(items, item => { return {}; }), b: _vOn(onClick) }
}
}`,
{ inline: false, mode: 'function', prefixIdentifiers: false }
@@ -46,11 +46,11 @@ return function render(_ctx, _cache) {
test('function mode preamble w/ prefixIdentifiers: true', () => {
assert(
``,
- ``,
+ ``,
`const { vOn: _vOn, vFor: _vFor } = Vue
return function render(_ctx, _cache) {
- return { a: _vFor(_ctx.items, item => { return { a: _vOn(_ctx.onClick) }; }) }
+ return { a: _vFor(_ctx.items, item => { return {}; }), b: _vOn(_ctx.onClick) }
}`,
{ inline: false, mode: 'function' }
)
diff --git a/packages/uni-mp-compiler/__tests__/scope.spec.ts b/packages/uni-mp-compiler/__tests__/scope.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c2dad134f05c341fb3b58132219f9ced623719b1
--- /dev/null
+++ b/packages/uni-mp-compiler/__tests__/scope.spec.ts
@@ -0,0 +1,56 @@
+import { assert } from './testUtils'
+
+describe('compiler: scope', () => {
+ test('v-for', () => {
+ assert(
+ `{{item.title}}`,
+ `{{item.a}}`,
+ `(_ctx, _cache) => {
+ return { a: _vFor(_ctx.items, item => { return { a: _toDisplayString(item.title), b: item.id, c: item.isRed, d: _vOn($event => _ctx.onClick(item)) }; }), b: _vOn(_ctx.longpress) }
+}`
+ )
+ })
+ test('v-for + v-for', () => {
+ assert(
+ `{{item.title}}{{handle(foo)}}{{item.id}}{{item1.title}}`,
+ `{{item.a}}{{b}}{{item.c}}{{item1.a}}`,
+ `(_ctx, _cache) => {
+ return { a: _vFor(_ctx.items, item => { return { a: _toDisplayString(item.title), b: _vFor(item.list, item1 => { return { a: _toDisplayString(item1.title), b: item1.id, c: _vOn($event => _ctx.longpress(item1)) }; }), c: _toDisplayString(item.id), d: _vOn($event => _ctx.onClick(item)), e: item.id }; }), b: _toDisplayString(_ctx.handle(_ctx.foo)) }
+}`
+ )
+ assert(
+ ``,
+ ``,
+ `(_ctx, _cache) => {
+ return { a: _vFor(_ctx.items, item => { return { a: item.id }; }), b: _vFor(_ctx.item1, item1 => { return { a: item1.title }; }) }
+}`
+ )
+ })
+ test('v-for + v-if', () => {
+ assert(
+ ``,
+ ``,
+ `(_ctx, _cache) => {
+ return { a: _vFor(_ctx.items, item => { return { ...(true ? { a: _ctx.id } : {}) }; }) }
+}`
+ )
+ })
+ test('v-if', () => {
+ assert(
+ `{{ok}}{{ok1}}{{ok2}}{{ok3}}`,
+ `{{b}}{{d}}{{f}}{{g}}`,
+ `(_ctx, _cache) => {
+ return { a: _ctx.ok, ...(_ctx.ok ? { b: _toDisplayString(_ctx.ok) } : _ctx.ok1 ? { d: _toDisplayString(_ctx.ok1) } : _ctx.ok2 ? { f: _toDisplayString(_ctx.ok2) } : { g: _toDisplayString(_ctx.ok3) }), c: _ctx.ok1, e: _ctx.ok2 }
+}`
+ )
+ })
+ test('v-if + v-for', () => {
+ assert(
+ ``,
+ ``,
+ `(_ctx, _cache) => {
+ return { a: _ctx.ok, ...(_ctx.ok ? { b: _vFor(_ctx.items, item => { return { a: item.id, b: item.title }; }), c: _ctx.foo, d: _vOn(_ctx.onClick) } : _ctx.ok1 ? { f: _vFor(_ctx.items, item => { return { a: item.id, b: item.title }; }), g: _ctx.foo, h: _vOn(_ctx.onClick) } : { i: _vFor(_ctx.items, item => { return { a: item.id, b: item.title }; }), j: _ctx.foo, k: _vOn(_ctx.onClick) }), e: _ctx.ok1 }
+}`
+ )
+ })
+})
diff --git a/packages/uni-mp-compiler/__tests__/test.spec.ts b/packages/uni-mp-compiler/__tests__/test.spec.ts
index c51865fe03b49bb422dcf1a5c6d6c78bb77e74e2..e4d7daa737a1c0a87c28e034259a3f9b0fdeaffa 100644
--- a/packages/uni-mp-compiler/__tests__/test.spec.ts
+++ b/packages/uni-mp-compiler/__tests__/test.spec.ts
@@ -7,7 +7,7 @@ function assert(
template: string,
templateCode: string,
renderCode: string,
- options: CompilerOptions
+ options: CompilerOptions = {}
) {
const res = compile(template, {
filename: 'foo.vue',
@@ -31,14 +31,13 @@ function assert(
}
describe('compiler', () => {
- test('should wrap as function if expression is inline statement', () => {
+ test('scope', () => {
assert(
- ``,
- ``,
+ ``,
+ ``,
`(_ctx, _cache) => {
- return { a: _normalizeClass({ red: _ctx.red }) }
-}`,
- {}
+ return { a: _vFor(_ctx.items, item => { return { a: item.id }; }), b: _vFor(_ctx.item1, item1 => { return { a: item1.title }; }) }
+}`
)
})
})
diff --git a/packages/uni-mp-compiler/__tests__/vFor.spec.ts b/packages/uni-mp-compiler/__tests__/vFor.spec.ts
index 0b1a3093a5f513e27df6d7eff1243844bcfa5a0b..a8fb2c7dc09fe25035ed82c4e2c1f2a91a24a2ef 100644
--- a/packages/uni-mp-compiler/__tests__/vFor.spec.ts
+++ b/packages/uni-mp-compiler/__tests__/vFor.spec.ts
@@ -199,9 +199,9 @@ describe(`compiler: v-for`, () => {
test(`v-if + v-for`, () => {
assert(
``,
- ``,
+ ``,
`(_ctx, _cache) => {
- return { b: _ctx.ok, ...(_ctx.ok ? { a: _vFor(_ctx.list, i => { return {}; }) } : {}) }
+ return { a: _ctx.ok, ...(_ctx.ok ? { b: _vFor(_ctx.list, i => { return {}; }) } : {}) }
}`
)
})
@@ -209,9 +209,9 @@ describe(`compiler: v-for`, () => {
test(`v-if + v-for on `, () => {
assert(
``,
- ``,
+ ``,
`(_ctx, _cache) => {
- return { b: _ctx.ok, ...(_ctx.ok ? { a: _vFor(_ctx.list, i => { return {}; }) } : {}) }
+ return { a: _ctx.ok, ...(_ctx.ok ? { b: _vFor(_ctx.list, i => { return {}; }) } : {}) }
}`
)
})
diff --git a/packages/uni-mp-compiler/__tests__/vIf.spec.ts b/packages/uni-mp-compiler/__tests__/vIf.spec.ts
index 84c2c755ba7262c8ddf054d6d4cac4b22ee8ac84..ce4f100aac5a44a5a8f1640c096369a0ac844fc6 100644
--- a/packages/uni-mp-compiler/__tests__/vIf.spec.ts
+++ b/packages/uni-mp-compiler/__tests__/vIf.spec.ts
@@ -161,9 +161,9 @@ describe(`compiler: v-if`, () => {
`,
- ``,
+ ``,
`(_ctx, _cache) => {
- return { b: _ctx.ok, ...(_ctx.ok ? { a: _ctx.ok2, ...(_ctx.ok2 ? {} : {}) } : {}) }
+ return { a: _ctx.ok, ...(_ctx.ok ? { b: _ctx.ok2, ...(_ctx.ok2 ? {} : {}) } : {}) }
}`
)
})
diff --git a/packages/uni-mp-compiler/src/options.ts b/packages/uni-mp-compiler/src/options.ts
index 56e563c4f4a1e1578199159e7d028b58ef360bdb..8a5ff9ae2cfa61f84c57604388fb1a1259346d57 100644
--- a/packages/uni-mp-compiler/src/options.ts
+++ b/packages/uni-mp-compiler/src/options.ts
@@ -34,15 +34,16 @@ export interface CodegenRootScope {
id: IdentifierGenerator
identifiers: string[]
properties: (ObjectProperty | SpreadElement)[]
+ parent: CodegenScope | null
}
export interface CodegenVIfScopeInit {
name: 'if' | 'else-if' | 'else' | string
condition?: Expression
}
export type CodegenVForScopeInit = VForOptions & { locals: string[] }
-export interface CodegenVIfScope
- extends CodegenRootScope,
- CodegenVIfScopeInit {}
+export interface CodegenVIfScope extends CodegenRootScope, CodegenVIfScopeInit {
+ parentScope: CodegenRootScope | CodegenVForScope
+}
export interface CodegenVForScope
extends CodegenRootScope,
CodegenVForScopeInit {}
diff --git a/packages/uni-mp-compiler/src/transform.ts b/packages/uni-mp-compiler/src/transform.ts
index 0691eea0ab49b28fe41113567de323beff0a4251..cb515791504d549ccd6772e662e201b641853113 100644
--- a/packages/uni-mp-compiler/src/transform.ts
+++ b/packages/uni-mp-compiler/src/transform.ts
@@ -211,10 +211,21 @@ export function createTransformContext(
onWarn = defaultOnWarn,
}: TransformOptions
): TransformContext {
- const scope: CodegenRootScope = {
+ const rootScope: CodegenRootScope = {
id: new IdentifierGenerator(),
identifiers: [],
properties: [],
+ parent: null,
+ }
+
+ function findVIfParentScope(): CodegenVForScope | CodegenRootScope {
+ for (let i = scopes.length - 1; i >= 0; i--) {
+ const scope = scopes[i]
+ if (isVForScope(scope) || isRootScope(scope)) {
+ return scope
+ }
+ }
+ return rootScope
}
function createScope(
@@ -225,6 +236,7 @@ export function createTransformContext(
{
id,
properties: [],
+ parent: scopes[scopes.length - 1],
get identifiers() {
return Object.keys(identifiers)
},
@@ -232,8 +244,9 @@ export function createTransformContext(
initScope
)
}
+
const identifiers = Object.create(null)
- const scopes: CodegenScope[] = [scope]
+ const scopes: CodegenScope[] = [rootScope]
const nameMatch = filename.replace(/\?.*$/, '').match(/([^/\\]+)\.\w+$/)
const context: TransformContext = {
// options
@@ -258,7 +271,7 @@ export function createTransformContext(
components: new Set(),
cached: 0,
identifiers,
- scope,
+ scope: rootScope,
scopes: {
vFor: 0,
},
@@ -274,8 +287,8 @@ export function createTransformContext(
addVIfScope(initScope) {
const vIfScope = createScope(
scopes[scopes.length - 1].id,
- initScope
- ) as CodegenVIfScope
+ extend(initScope, { parentScope: findVIfParentScope() })
+ ) as unknown as CodegenVIfScope
scopes.push(vIfScope)
return vIfScope
},
diff --git a/packages/uni-mp-compiler/src/transforms/utils.ts b/packages/uni-mp-compiler/src/transforms/utils.ts
index 665b92f35a7a2cff650101c3a63b518658099156..c2af6a574cf648c3ce0fe1255fb2077bc8258e3d 100644
--- a/packages/uni-mp-compiler/src/transforms/utils.ts
+++ b/packages/uni-mp-compiler/src/transforms/utils.ts
@@ -12,19 +12,13 @@ import {
createSimpleExpression,
ExpressionNode,
NodeTypes,
- SimpleExpressionNode,
SourceLocation,
} from '@vue/compiler-core'
import { walk, BaseNode } from 'estree-walker'
import { isUndefined, parseExpr } from '../ast'
import { genBabelExpr, genExpr } from '../codegen'
import { CodegenScope, CodegenVForScope } from '../options'
-import {
- isRootScope,
- isVForScope,
- isVIfScope,
- TransformContext,
-} from '../transform'
+import { isVForScope, isVIfScope, TransformContext } from '../transform'
export function rewriteSpreadElement(
name: symbol,
@@ -80,50 +74,34 @@ export function rewriteExpression(
return createSimpleExpression('undefined', false, node.loc)
}
- scope = findScope(babelNode, scope)!
+ scope = findReferencedScope(babelNode, scope)
const id = scope.id.next()
scope.properties.push(objectProperty(identifier(id), babelNode!))
- if (node.type === NodeTypes.COMPOUND_EXPRESSION) {
- const firstChild = node.children[0]
- if (isSimpleExpression(firstChild)) {
- const content = firstChild.content.trim()
- if (scope.identifiers.includes(content)) {
- return createSimpleExpression(content + '.' + id)
- }
+ // 在v-for中包含的v-if块,所有变量需要补充当前v-for value前缀
+ if (isVIfScope(scope)) {
+ if (isVForScope(scope.parentScope)) {
+ return createSimpleExpression(scope.parentScope.valueAlias + '.' + id)
}
+ return createSimpleExpression(id)
+ } else if (isVForScope(scope)) {
+ return createSimpleExpression(scope.valueAlias + '.' + id)
}
return createSimpleExpression(id)
}
-// function findReferencedScope(
-// node: Expression,
-// scope: CodegenScope
-// ): CodegenRootScope | CodegenVForScope {
-// if (isRootScope(scope)) {
-// return scope
-// }
-
-// }
-
-function findScope(node: Expression, scope: CodegenScope) {
- if (isRootScope(scope) || isVIfScope(scope)) {
- return scope
- }
- return findVForScope(node, scope) || scope
-}
-
-function findVForScope(
+function findReferencedScope(
node: Expression,
scope: CodegenScope
-): CodegenVForScope | undefined {
- if (isVForScope(scope)) {
+): CodegenScope {
+ if (isVIfScope(scope)) {
+ return scope
+ } else if (isVForScope(scope)) {
if (isReferencedScope(node, scope)) {
return scope
}
+ return findReferencedScope(node, scope.parent!)
}
- // if (scope.parent) {
- // return findVForScope(node, scope.parent)
- // }
+ return scope
}
function isReferencedScope(node: Expression, scope: CodegenVForScope) {
@@ -149,7 +127,3 @@ function isReferencedScope(node: Expression, scope: CodegenVForScope) {
})
return referenced
}
-
-function isSimpleExpression(val: any): val is SimpleExpressionNode {
- return val.type && val.type === NodeTypes.SIMPLE_EXPRESSION
-}
diff --git a/packages/uni-mp-compiler/src/transforms/vFor.ts b/packages/uni-mp-compiler/src/transforms/vFor.ts
index cc41a3a5ee3970e1616d643bb4fa82eb4b543c1b..ea0f48d464bc81f625e7898ef108fda291516225 100644
--- a/packages/uni-mp-compiler/src/transforms/vFor.ts
+++ b/packages/uni-mp-compiler/src/transforms/vFor.ts
@@ -1,4 +1,4 @@
-import { isString } from '@vue/shared'
+import { extend, isString } from '@vue/shared'
import {
createCompilerError,
createSimpleExpression,
@@ -14,22 +14,19 @@ import {
isTemplateNode,
findProp,
} from '@vue/compiler-core'
-import {
- createObjectProperty,
- createVForCallExpression,
- parseExpr,
- parseParam,
-} from '../ast'
+import { createVForCallExpression, parseExpr, parseParam } from '../ast'
import { NodeTransform, TransformContext } from '../transform'
import { processExpression } from './transformExpression'
import { genExpr } from '../codegen'
import {
+ cloneNode,
Expression,
Identifier,
isIdentifier,
Pattern,
RestElement,
} from '@babel/types'
+import { rewriteExpression } from './utils'
export type VForOptions = Omit & {
sourceExpr?: Expression
@@ -119,11 +116,20 @@ export const transformFor = createStructuralDirectiveTransform(
...vForData,
locals: findVForLocals(parseResult),
})
+ // 先占位vFor,后续更新 cloneSourceExpr 为 CallExpression
+ const cloneSourceExpr = cloneNode(sourceExpr!, false)
const vFor = {
...vForData,
+ sourceAlias: rewriteExpression(
+ source,
+ context,
+ cloneSourceExpr,
+ parentScope
+ ).content,
}
;(node as ForElementNode).vFor = vFor
scopes.vFor++
+
return () => {
if (isTemplateNode(node)) {
node.children.some((c) => {
@@ -146,16 +152,22 @@ export const transformFor = createStructuralDirectiveTransform(
key && removeIdentifiers(key)
index && removeIdentifiers(index)
}
- const id = parentScope.id.next()
- vFor.sourceAlias = id
- parentScope.properties.push(
- createObjectProperty(id, createVForCallExpression(vForScope, context))
+ extend(
+ clearExpr(cloneSourceExpr),
+ createVForCallExpression(vForScope, context)
)
popScope()
}
}
) as unknown as NodeTransform
+function clearExpr(expr: Expression) {
+ Object.keys(expr).forEach((key) => {
+ delete (expr as any)[key]
+ })
+ return expr
+}
+
function parseAlias(
babelExpr: Identifier | Pattern | RestElement,
exprCode: string,
diff --git a/packages/uni-mp-compiler/src/transforms/vIf.ts b/packages/uni-mp-compiler/src/transforms/vIf.ts
index ab34f4137bec2c0328d038cc61f7235dc3a9d811..f879198075800320eeb2a5cf92555020ef6916ba 100644
--- a/packages/uni-mp-compiler/src/transforms/vIf.ts
+++ b/packages/uni-mp-compiler/src/transforms/vIf.ts
@@ -54,19 +54,19 @@ export const transformIf = createStructuralDirectiveTransform(
name: dir.name,
condition,
})
- return () => {
- if (condition) {
- if (!isLiteral(condition)) {
- ifOptions.condition = rewriteExpression(
- dir.exp!,
- context,
- condition,
- parentScope
- ).content
- } else {
- ifOptions.condition = (dir.exp as SimpleExpressionNode).content
- }
+ if (condition) {
+ if (!isLiteral(condition)) {
+ ifOptions.condition = rewriteExpression(
+ dir.exp!,
+ context,
+ condition,
+ parentScope
+ ).content
+ } else {
+ ifOptions.condition = (dir.exp as SimpleExpressionNode).content
}
+ }
+ return () => {
if (isRoot) {
parentScope.properties.push(createVIfSpreadElement(vIfScope))
} else {