提交 f5ba2ed0 编写于 作者: fxy060608's avatar fxy060608

fix(mp): v-for + v-for + v-if (#3145)

上级 b88cc38e
......@@ -39,6 +39,54 @@ describe('compiler: scope', () => {
`<view wx:for="{{a}}" wx:for-item="item"><view wx:if="{{true}}" data-id="{{item.a}}"></view></view>`,
`(_ctx, _cache) => {
return { a: _f(_ctx.items, (item, k0, i0) => { return true ? { a: _ctx.id } : {}; }) }
}`
)
})
test('v-for + v-for + v-if', () => {
assert(
`<view v-for="s in items1"><view v-for="r in items2"><text v-if="s == 2">s</text></view></view>`,
`<view wx:for="{{a}}" wx:for-item="s"><view wx:for="{{s.a}}" wx:for-item="r"><text wx:if="{{s.b}}">s</text></view></view>`,
`(_ctx, _cache) => {
return { a: _f(_ctx.items1, (s, k0, i0) => { return { a: _f(_ctx.items2, (r, k1, i1) => { return s == 2 ? {} : {}; }), b: s == 2 }; }) }
}`
)
assert(
`<view v-for="s in items1"><view v-for="r in items2"><text v-if="a == 2">s</text></view></view>`,
`<view wx:for="{{a}}" wx:for-item="s"><view wx:for="{{b}}" wx:for-item="r"><text wx:if="{{c}}">s</text></view></view>`,
`(_ctx, _cache) => {
return { a: _f(_ctx.items1, (s, k0, i0) => { return {}; }), b: _f(_ctx.items2, (r, k1, i1) => { return _ctx.a == 2 ? {} : {}; }), c: _ctx.a == 2 }
}`
)
})
test('v-for + v-for + v-if + v-else-if', () => {
assert(
`<view v-for="s in items1"><view v-for="r in items2"><text v-if="a == 2">a</text><text v-else-if="s == 3">s</text></view></view>`,
`<view wx:for="{{a}}" wx:for-item="s"><view wx:for="{{s.a}}" wx:for-item="r"><text wx:if="{{b}}">a</text><text wx:elif="{{s.b}}">s</text></view></view>`,
`(_ctx, _cache) => {
return { a: _f(_ctx.items1, (s, k0, i0) => { return { a: _f(_ctx.items2, (r, k1, i1) => { return _ctx.a == 2 ? {} : s == 3 ? {} : {}; }), b: s == 3 }; }), b: _ctx.a == 2 }
}`
)
assert(
`<view v-for="s in items1"><view v-for="r in items2"><text v-if="a == 2">a</text><text v-else-if="b == 3">s</text></view></view>`,
`<view wx:for="{{a}}" wx:for-item="s"><view wx:for="{{b}}" wx:for-item="r"><text wx:if="{{c}}">a</text><text wx:elif="{{d}}">s</text></view></view>`,
`(_ctx, _cache) => {
return { a: _f(_ctx.items1, (s, k0, i0) => { return {}; }), b: _f(_ctx.items2, (r, k1, i1) => { return _ctx.a == 2 ? {} : _ctx.b == 3 ? {} : {}; }), c: _ctx.a == 2, d: _ctx.b == 3 }
}`
)
})
test('v-for + v-for + v-for + v-if', () => {
assert(
`<view v-for="s in items1"><view v-for="r in items2"><view v-for="t in items3"><text v-if="s == 2">s</text></view></view></view>`,
`<view wx:for="{{a}}" wx:for-item="s"><view wx:for="{{b}}" wx:for-item="r"><view wx:for="{{s.a}}" wx:for-item="t"><text wx:if="{{s.b}}">s</text></view></view></view>`,
`(_ctx, _cache) => {
return { a: _f(_ctx.items1, (s, k0, i0) => { return { a: _f(_ctx.items3, (t, k2, i2) => { return s == 2 ? {} : {}; }), b: s == 2 }; }), b: _f(_ctx.items2, (r, k1, i1) => { return {}; }) }
}`
)
assert(
`<view v-for="s in items1"><view v-for="r in items2"><view v-for="t in items3"><text v-if="r == 2">s</text></view></view></view>`,
`<view wx:for="{{a}}" wx:for-item="s"><view wx:for="{{b}}" wx:for-item="r"><view wx:for="{{r.a}}" wx:for-item="t"><text wx:if="{{r.b}}">s</text></view></view></view>`,
`(_ctx, _cache) => {
return { a: _f(_ctx.items1, (s, k0, i0) => { return {}; }), b: _f(_ctx.items2, (r, k1, i1) => { return { a: _f(_ctx.items3, (t, k2, i2) => { return r == 2 ? {} : {}; }), b: r == 2 }; }) }
}`
)
})
......
......@@ -117,6 +117,7 @@ export interface TransformContext
addIdentifiers(exp: ExpressionNode | string): void
removeIdentifiers(exp: ExpressionNode | string): void
popScope(): CodegenScope | undefined
getScopeIndex(scope: CodegenScope): number
addVIfScope(initScope: CodegenVIfScopeInit): CodegenVIfScope
addVForScope(initScope: CodegenVForScopeInit): CodegenVForScope
cache<T extends JSChildNode>(exp: T, isVNode?: boolean): CacheExpression | T
......@@ -377,6 +378,9 @@ export function createTransformContext(
return false
},
// methods
getScopeIndex(scope: CodegenScope) {
return scopes.indexOf(scope)
},
popScope() {
return scopes.pop()
},
......
......@@ -107,7 +107,18 @@ export function rewriteExpression(
context: TransformContext | VueTransformContext,
babelNode?: Expression,
scope: CodegenScope = (context as TransformContext).currentScope,
{ property, ignoreLiteral } = { property: true, ignoreLiteral: false }
{
property,
ignoreLiteral,
referencedScope,
}: {
property: boolean
ignoreLiteral: boolean
referencedScope?: CodegenScope
} = {
property: true,
ignoreLiteral: false,
}
) {
if (node.type === NodeTypes.SIMPLE_EXPRESSION && node.isStatic) {
return node
......@@ -133,24 +144,27 @@ export function rewriteExpression(
}
}
scope = findReferencedScope(babelNode, scope)
const id = scope.id.next()
referencedScope = referencedScope || findReferencedScope(babelNode, scope)
const id = referencedScope!.id.next()
if (property) {
scope.properties.push(objectProperty(identifier(id), babelNode!))
referencedScope!.properties.push(objectProperty(identifier(id), babelNode!))
}
// 在v-for中包含的v-if块,所有变量需要补充当前v-for value前缀
if (isVIfScope(scope)) {
if (isVForScope(scope.parentScope)) {
return createSimpleExpression(scope.parentScope.valueAlias + '.' + id)
// 在 v-for 中包含的 v-if 块,所有变量需要补充当前 v-for value 前缀
if (isVIfScope(referencedScope!)) {
if (isVForScope(referencedScope.parentScope)) {
return createSimpleExpression(
referencedScope.parentScope.valueAlias + '.' + id
)
}
return createSimpleExpression(id)
} else if (isVForScope(scope)) {
return createSimpleExpression(scope.valueAlias + '.' + id)
} else if (isVForScope(referencedScope!)) {
return createSimpleExpression(referencedScope.valueAlias + '.' + id)
}
return createSimpleExpression(id)
}
function findReferencedScope(
export function findReferencedScope(
node: Expression,
scope: CodegenScope
): CodegenScope {
......@@ -165,7 +179,7 @@ function findReferencedScope(
return scope
}
function isReferencedByIds(node: Expression, knownIds: string[]) {
export function isReferencedByIds(node: Expression, knownIds: string[]) {
let referenced = false
walk(node as unknown as BaseNode, {
enter(node: BaseNode, parent: BaseNode) {
......
......@@ -13,11 +13,14 @@ import {
isTemplateNode,
findProp,
ComponentNode,
findDir,
TemplateChildNode,
} from '@vue/compiler-core'
import { parseExpr, parseParam } from '../ast'
import {
createStructuralDirectiveTransform,
isScopedSlotVFor,
isVForScope,
NodeTransform,
TransformContext,
} from '../transform'
......@@ -37,10 +40,15 @@ import {
RestElement,
returnStatement,
} from '@babel/types'
import { rewriteExpression } from './utils'
import { CodegenVForScope } from '../options'
import {
findReferencedScope,
isReferencedByIds,
rewriteExpression,
} from './utils'
import { CodegenScope, CodegenVForScope } from '../options'
import { V_FOR } from '../runtimeHelpers'
import { createVSlotCallExpression } from './vSlot'
import { isElementNode } from '@dcloudio/uni-cli-shared'
export type VForOptions = Omit<ForParseResult, 'tagType'> & {
sourceExpr?: Expression
......@@ -106,13 +114,36 @@ export const transformFor = createStructuralDirectiveTransform(
const indexAlias = parseAlias(indexExpr, indexCode, 'i' + scopes.vFor)
// 先占位 vFor,后续更新 cloneSourceExpr 为 CallExpression
const cloneSourceExpr = cloneNode(sourceExpr!, false)
const sourceAliasReferencedScope = findReferencedScope(
cloneSourceExpr,
context.currentScope
)
// 寻找子节点中 if 指令作用域
const vIfReferencedScope = findVIfReferencedScope(
node,
context.currentScope,
context
)
// 取最近的作用域
const referencedScope =
vIfReferencedScope &&
context.getScopeIndex(vIfReferencedScope) >
context.getScopeIndex(sourceAliasReferencedScope)
? vIfReferencedScope
: sourceAliasReferencedScope
const sourceAlias = rewriteExpression(
source,
context,
cloneSourceExpr,
parentScope,
// 强制 rewrite,因为即使是字符串,数字,也要走 vFor 函数
{ property: true, ignoreLiteral: true }
{
property: true,
ignoreLiteral: true,
referencedScope,
}
).content
const sourceCode = `{{${sourceAlias}}}`
const vForData: VForOptions = {
......@@ -204,6 +235,75 @@ function parseAlias(
return fallback
}
function parseVForScope(currentScope: CodegenScope) {
while (currentScope) {
if (isVForScope(currentScope) && !isScopedSlotVFor(currentScope)) {
return currentScope
}
currentScope = currentScope.parent!
}
}
function findVIfReferencedScope(
node: ElementNode,
currentScope: CodegenScope | null,
context: TransformContext
): CodegenVForScope | undefined {
if (!currentScope) {
return
}
const vForScope = parseVForScope(currentScope)
if (!vForScope) {
return
}
if (
!node.children.find((item) => checkVIfReferenced(item, vForScope, context))
) {
return findVIfReferencedScope(node, currentScope.parent, context)
}
return vForScope
}
function checkVIfReferenced(
node: TemplateChildNode,
vForScope: CodegenVForScope,
context: TransformContext
): boolean {
if (!isElementNode(node)) {
return false
}
// 嵌套 for 不查找
if (findDir(node, 'for')) {
return false
}
const ifDir = findDir(node, 'if')
if (ifDir) {
return checkDirReferenced(ifDir.exp, vForScope, context)
}
const elseIfDir = findDir(node, 'else-if')
if (elseIfDir) {
return checkDirReferenced(elseIfDir.exp, vForScope, context)
}
return !!node.children.find((item) =>
checkVIfReferenced(item, vForScope, context)
)
}
function checkDirReferenced(
node: ExpressionNode | undefined,
vForScope: CodegenVForScope,
context: TransformContext
) {
if (node) {
const babelNode = parseExpr(node, context)
if (babelNode && isReferencedByIds(babelNode, vForScope.locals)) {
return true
}
}
return false
}
function findVForLocals({ value, key, index }: ForParseResult) {
const ids: string[] = []
if (value) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册