diff --git a/packages/uni-mp-alipay/__tests__/vSlot.spec.ts b/packages/uni-mp-alipay/__tests__/vSlot.spec.ts
index 47c20fe8e74062a556a112e6b251805f809c0de2..463469b2bf211884f72c958b4cc80a3ad40d92c4 100644
--- a/packages/uni-mp-alipay/__tests__/vSlot.spec.ts
+++ b/packages/uni-mp-alipay/__tests__/vSlot.spec.ts
@@ -27,9 +27,9 @@ describe('mp-alipay: transform v-slot', () => {
)
assert(
`{{error.message}}`,
- `{{v0.b}}`,
+ `{{v0.b}}`,
`(_ctx, _cache) => {
- return { a: _w(({ data, loading, error, options }, s0, i0) => { return _e({ a: error }, error ? { b: _t(error.message) } : {}, { c: s0 }); }, { name: 'default', vueId: '2a9ec0b0-0' }) }
+ return { a: _w(({ data, loading, error, options }, s0, i0) => { return _e({ a: error }, error ? { b: _t(error.message) } : {}, { c: s0, d: i0 }); }, { name: 'default', vueId: '2a9ec0b0-0' }) }
}`
)
})
@@ -37,9 +37,9 @@ describe('mp-alipay: transform v-slot', () => {
test('scoped slots', () => {
assert(
`{{ slotProps.item }}`,
- `{{slotProps.a}}`,
+ `{{slotProps.a}}`,
`(_ctx, _cache) => {
- return { a: _w((slotProps, s0, i0) => { return { a: _t(slotProps.item), b: s0 }; }, { name: 'default', vueId: '2a9ec0b0-0' }) }
+ return { a: _w((slotProps, s0, i0) => { return { a: _t(slotProps.item), b: s0, c: i0 }; }, { name: 'default', vueId: '2a9ec0b0-0' }) }
}`
)
})
@@ -47,9 +47,9 @@ describe('mp-alipay: transform v-slot', () => {
test('scoped slots + scoped slots', () => {
assert(
`{{ slotProps.item }}{{ slotProps1.item }}`,
- `{{slotProps.b}}{{slotProps1.a}}`,
+ `{{slotProps.b}}{{slotProps1.a}}`,
`(_ctx, _cache) => {
- return { a: _w((slotProps, s0, i0) => { return { a: _w((slotProps1, s1, i1) => { return { a: _t(slotProps1.item), b: s1 }; }, { name: 'default', vueId: '2a9ec0b0-1' + '-' + i0 + ',' + '2a9ec0b0-0' }), b: _t(slotProps.item), c: '2a9ec0b0-1' + '-' + i0 + ',' + '2a9ec0b0-0', d: s0 }; }, { name: 'default', vueId: '2a9ec0b0-0' }) }
+ return { a: _w((slotProps, s0, i0) => { return { a: _w((slotProps1, s1, i1) => { return { a: _t(slotProps1.item), b: s1, c: i1 }; }, { name: 'default', vueId: '2a9ec0b0-1' + '-' + i0 + ',' + '2a9ec0b0-0' }), b: _t(slotProps.item), c: '2a9ec0b0-1' + '-' + i0 + ',' + '2a9ec0b0-0', d: s0, e: i0 }; }, { name: 'default', vueId: '2a9ec0b0-0' }) }
}`
)
})
@@ -57,9 +57,9 @@ describe('mp-alipay: transform v-slot', () => {
test('v-if + scoped slots', () => {
assert(
`{{ slotProps.item }}`,
- `{{slotProps.a}}`,
+ `{{slotProps.a}}`,
`(_ctx, _cache) => {
- return _e({ a: _ctx.ok }, _ctx.ok ? { b: _w((slotProps, s0, i0) => { return { a: _t(slotProps.item), b: s0 }; }, { name: 'default', vueId: '2a9ec0b0-0' }) } : {})
+ return _e({ a: _ctx.ok }, _ctx.ok ? { b: _w((slotProps, s0, i0) => { return { a: _t(slotProps.item), b: s0, c: i0 }; }, { name: 'default', vueId: '2a9ec0b0-0' }) } : {})
}`
)
})
@@ -67,9 +67,9 @@ describe('mp-alipay: transform v-slot', () => {
test('v-for + scoped slots', () => {
assert(
`{{ slotProps.item }}`,
- `{{slotProps.a}}`,
+ `{{slotProps.a}}`,
`(_ctx, _cache) => {
- return { a: _f(_ctx.items, (item, k0, i0) => { return { a: _w((slotProps, s1, i1) => { return { a: _t(slotProps.item), b: s1 }; }, { name: 'default', vueId: '2a9ec0b0-0' + '-' + i0 }), b: '2a9ec0b0-0' + '-' + i0 }; }) }
+ return { a: _f(_ctx.items, (item, k0, i0) => { return { a: _w((slotProps, s1, i1) => { return { a: _t(slotProps.item), b: s1, c: i1 }; }, { name: 'default', vueId: '2a9ec0b0-0' + '-' + i0 }), b: '2a9ec0b0-0' + '-' + i0 }; }) }
}`
)
})
@@ -77,16 +77,16 @@ describe('mp-alipay: transform v-slot', () => {
test('v-for + v-for + scoped slots', () => {
assert(
`{{ slotProps.item }}`,
- `{{slotProps.a}}`,
+ `{{slotProps.a}}`,
`(_ctx, _cache) => {
- return { a: _f(_ctx.items, (item, k0, i0) => { return { a: _f(item.list, (item1, k1, i1) => { return { a: _w((slotProps, s2, i2) => { return { a: _t(slotProps.item), b: s2 }; }, { name: 'default', vueId: '2a9ec0b0-0' + '-' + i0 + '-' + i1 }), b: item1, c: '2a9ec0b0-0' + '-' + i0 + '-' + i1 }; }) }; }) }
+ return { a: _f(_ctx.items, (item, k0, i0) => { return { a: _f(item.list, (item1, k1, i1) => { return { a: _w((slotProps, s2, i2) => { return { a: _t(slotProps.item), b: s2, c: i2 }; }, { name: 'default', vueId: '2a9ec0b0-0' + '-' + i0 + '-' + i1 }), b: item1, c: '2a9ec0b0-0' + '-' + i0 + '-' + i1 }; }) }; }) }
}`
)
})
test('old syntax', () => {
assert(
``,
- ``,
+ ``,
`(_ctx, _cache) => {
return {}
}`
diff --git a/packages/uni-mp-baidu/__tests__/vFor.spec.ts b/packages/uni-mp-baidu/__tests__/vFor.spec.ts
index af48124a607a571dff67cfc34424146410ecb5f0..2d4a84b76b6619eb9b5fa26bb721e5e867f7bd38 100644
--- a/packages/uni-mp-baidu/__tests__/vFor.spec.ts
+++ b/packages/uni-mp-baidu/__tests__/vFor.spec.ts
@@ -4,9 +4,9 @@ describe(`mp-baidu: transform v-for`, () => {
test(`with key`, () => {
assert(
``,
- ``,
+ ``,
`(_ctx, _cache) => {
- return { a: _f(_ctx.items, (item, k0, i0) => { return {}; }) }
+ return { a: _f(_ctx.items, (item, k0, i0) => { return { a: item.id }; }) }
}`
)
})
diff --git a/packages/uni-mp-baidu/dist/uni.compiler.js b/packages/uni-mp-baidu/dist/uni.compiler.js
index 4d2c6982c75c82672ccd75f3af4d822ef734ce28..1abb9a515b710e7adf568e77d3f6924408f0dd6d 100644
--- a/packages/uni-mp-baidu/dist/uni.compiler.js
+++ b/packages/uni-mp-baidu/dist/uni.compiler.js
@@ -37,7 +37,7 @@ var source = {
setting: setting
};
-const transformFor = (node) => {
+const transformFor = (node, context) => {
if (!uniMpCompiler.isForElementNode(node)) {
return;
}
@@ -45,7 +45,7 @@ const transformFor = (node) => {
if (keyProp) {
const { exp } = keyProp;
if (exp) {
- const key = uniMpCompiler.genExpr(exp);
+ const key = uniMpCompiler.rewriteExpression(exp, context).content;
node.vFor.sourceCode = `${node.vFor.sourceAlias} trackBy ${key}`;
node.props.splice(node.props.indexOf(keyProp), 1);
}
diff --git a/packages/uni-mp-baidu/src/compiler/transforms/vFor.ts b/packages/uni-mp-baidu/src/compiler/transforms/vFor.ts
index aef36574457057e1dc4cf8dfa9d6222e63dec76a..6b7724b70032c91294de33446fbb1f4a5907a360 100644
--- a/packages/uni-mp-baidu/src/compiler/transforms/vFor.ts
+++ b/packages/uni-mp-baidu/src/compiler/transforms/vFor.ts
@@ -1,12 +1,12 @@
import {
- genExpr,
findProp,
isForElementNode,
DirectiveNode,
NodeTransform,
+ rewriteExpression,
} from '@dcloudio/uni-mp-compiler'
-export const transformFor: NodeTransform = (node) => {
+export const transformFor: NodeTransform = (node, context) => {
if (!isForElementNode(node)) {
return
}
@@ -14,7 +14,7 @@ export const transformFor: NodeTransform = (node) => {
if (keyProp) {
const { exp } = keyProp as DirectiveNode
if (exp) {
- const key = genExpr(exp)
+ const key = rewriteExpression(exp, context).content
node.vFor.sourceCode = `${node.vFor.sourceAlias} trackBy ${key}`
node.props.splice(node.props.indexOf(keyProp), 1)
}
diff --git a/packages/uni-mp-compiler/__tests__/scopedSlot.spec.ts b/packages/uni-mp-compiler/__tests__/scopedSlot.spec.ts
index 8630fdfb990e07bd081d89406cdd35d9a941bbeb..58a31f33284cb90c330cd7b09ba8f8b0c65adc76 100644
--- a/packages/uni-mp-compiler/__tests__/scopedSlot.spec.ts
+++ b/packages/uni-mp-compiler/__tests__/scopedSlot.spec.ts
@@ -4,7 +4,7 @@ describe('compiler: transform scoped slots', () => {
test('basic', () => {
assert(
``,
- ``,
+ ``,
`(_ctx, _cache) => {
return { a: _r("default", { item: _ctx.item, index: _ctx.index }) }
}`
diff --git a/packages/uni-mp-compiler/__tests__/slot.spec.ts b/packages/uni-mp-compiler/__tests__/slot.spec.ts
index 1c4a0285969e90ee247245b07fc0a8d41dc7799c..e6864a5979b16d3625bb768b91639dd084fe2e81 100644
--- a/packages/uni-mp-compiler/__tests__/slot.spec.ts
+++ b/packages/uni-mp-compiler/__tests__/slot.spec.ts
@@ -40,9 +40,18 @@ describe('compiler: transform slot', () => {
test('slot with v-for', () => {
assert(
``,
- ``,
+ ``,
`(_ctx, _cache) => {
- return { a: _f(_ctx.items, (item, index, i0) => { return { a: _r(\"default\", { key: index }) }; }) }
+ return { a: _f(_ctx.items, (item, index, i0) => { return { a: "default-" + i0, b: _r("default", { key: index }, i0) }; }) }
+}`
+ )
+ })
+ test('slot with v-for + v-for', () => {
+ assert(
+ ``,
+ ``,
+ `(_ctx, _cache) => {
+ return { a: _f(_ctx.items, (item, index, i0) => { return { a: _f(item.list, (item1, index1, i1) => { return { a: "default-" + i0 + '-' + i1, b: _r("default", { key: index1 }, i0 + '-' + i1) }; }), b: index }; }) }
}`
)
})
diff --git a/packages/uni-mp-compiler/__tests__/test.spec.ts b/packages/uni-mp-compiler/__tests__/test.spec.ts
deleted file mode 100644
index e0d5168a9ce24fa4b99b36b0585088cbfc6ebfaa..0000000000000000000000000000000000000000
--- a/packages/uni-mp-compiler/__tests__/test.spec.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-// import { inspect } from './testUtils'
-
-import { compile } from '../src'
-import { CompilerOptions } from '../src/options'
-import { miniProgram } from './testUtils'
-
-function assert(
- template: string,
- templateCode: string,
- renderCode: string,
- options: CompilerOptions = {}
-) {
- const res = compile(template, {
- filename: 'foo.vue',
- prefixIdentifiers: true,
- inline: true,
- generatorOpts: {
- concise: true,
- },
- miniProgram: {
- ...miniProgram,
- emitFile({ source }) {
- console.log(source)
- // expect(source).toBe(templateCode)
- return ''
- },
- },
- ...options,
- })
- // expect(res.template).toBe(templateCode)
- // expect(res.code).toBe(renderCode)
- // console.log(require('util').inspect(res.code, { colors: true, depth: null }))
- // console.log(require('util').inspect(res, { colors: true, depth: null }))
- console.log(res.code)
- expect(res.code).toBe(renderCode)
-}
-
-describe('compiler', () => {
- test('scope', () => {
- assert(
- ``,
- ``,
- `(_ctx, _cache) => {
- return {}
-}`
- )
- })
-})
diff --git a/packages/uni-mp-compiler/__tests__/test.ts b/packages/uni-mp-compiler/__tests__/test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..27b2ceb430df9064c6584280db5cad579ef20648
--- /dev/null
+++ b/packages/uni-mp-compiler/__tests__/test.ts
@@ -0,0 +1,44 @@
+// import { inspect } from './testUtils'
+
+import { compile } from '../src/index'
+import { CompilerOptions } from '../src/options'
+import { miniProgram } from './testUtils'
+
+function assert(
+ template: string,
+ templateCode: string,
+ renderCode: string,
+ options: CompilerOptions = {}
+) {
+ const res = compile(template, {
+ filename: 'foo.vue',
+ prefixIdentifiers: true,
+ inline: true,
+ generatorOpts: {
+ concise: true,
+ },
+ miniProgram: {
+ ...miniProgram,
+ emitFile({ source }) {
+ console.log(source)
+ return ''
+ },
+ },
+ ...options,
+ })
+ console.log(res.code)
+ if (res.code === renderCode) {
+ console.log('success')
+ } else {
+ console.error('error')
+ console.error(renderCode)
+ }
+}
+
+assert(
+ ``,
+ ``,
+ `(_ctx, _cache) => {
+return { a: _f(_ctx.items, (item, index, i0) => { return { a: _r(\"default\", { key: index }) }; }) }
+}`
+)
diff --git a/packages/uni-mp-compiler/__tests__/vSlot.spec.ts b/packages/uni-mp-compiler/__tests__/vSlot.spec.ts
index 3708d1e7eedf37666f33929f9480a6c1cb31a6fe..40ca925e0f87f49827c9b8a0ec3422973d8027c0 100644
--- a/packages/uni-mp-compiler/__tests__/vSlot.spec.ts
+++ b/packages/uni-mp-compiler/__tests__/vSlot.spec.ts
@@ -27,9 +27,9 @@ describe('compiler: transform v-slot', () => {
)
assert(
`{{error.message}}`,
- `{{v0.b}}`,
+ `{{v0.b}}`,
`(_ctx, _cache) => {
- return { a: _w(({ data, loading, error, options }, s0, i0) => { return _e({ a: error }, error ? { b: _t(error.message) } : {}, { c: s0 }); }, { name: 'default', vueId: '2a9ec0b0-0' }) }
+ return { a: _w(({ data, loading, error, options }, s0, i0) => { return _e({ a: error }, error ? { b: _t(error.message) } : {}, { c: s0, d: i0 }); }, { name: 'default', vueId: '2a9ec0b0-0' }) }
}`
)
})
@@ -37,9 +37,9 @@ describe('compiler: transform v-slot', () => {
test('scoped slots', () => {
assert(
`{{ slotProps.item }}`,
- `{{slotProps.a}}`,
+ `{{slotProps.a}}`,
`(_ctx, _cache) => {
- return { a: _w((slotProps, s0, i0) => { return { a: _t(slotProps.item), b: s0 }; }, { name: 'default', vueId: '2a9ec0b0-0' }) }
+ return { a: _w((slotProps, s0, i0) => { return { a: _t(slotProps.item), b: s0, c: i0 }; }, { name: 'default', vueId: '2a9ec0b0-0' }) }
}`
)
})
@@ -47,9 +47,9 @@ describe('compiler: transform v-slot', () => {
test('scoped slots + scoped slots', () => {
assert(
`{{ slotProps.item }}{{ slotProps1.item }}`,
- `{{slotProps.b}}{{slotProps1.a}}`,
+ `{{slotProps.b}}{{slotProps1.a}}`,
`(_ctx, _cache) => {
- return { a: _w((slotProps, s0, i0) => { return { a: _w((slotProps1, s1, i1) => { return { a: _t(slotProps1.item), b: s1 }; }, { name: 'default', vueId: '2a9ec0b0-1' + '-' + i0 + ',' + '2a9ec0b0-0' }), b: _t(slotProps.item), c: '2a9ec0b0-1' + '-' + i0 + ',' + '2a9ec0b0-0', d: s0 }; }, { name: 'default', vueId: '2a9ec0b0-0' }) }
+ return { a: _w((slotProps, s0, i0) => { return { a: _w((slotProps1, s1, i1) => { return { a: _t(slotProps1.item), b: s1, c: i1 }; }, { name: 'default', vueId: '2a9ec0b0-1' + '-' + i0 + ',' + '2a9ec0b0-0' }), b: _t(slotProps.item), c: '2a9ec0b0-1' + '-' + i0 + ',' + '2a9ec0b0-0', d: s0, e: i0 }; }, { name: 'default', vueId: '2a9ec0b0-0' }) }
}`
)
})
@@ -57,9 +57,9 @@ describe('compiler: transform v-slot', () => {
test('v-if + scoped slots', () => {
assert(
`{{ slotProps.item }}`,
- `{{slotProps.a}}`,
+ `{{slotProps.a}}`,
`(_ctx, _cache) => {
- return _e({ a: _ctx.ok }, _ctx.ok ? { b: _w((slotProps, s0, i0) => { return { a: _t(slotProps.item), b: s0 }; }, { name: 'default', vueId: '2a9ec0b0-0' }) } : {})
+ return _e({ a: _ctx.ok }, _ctx.ok ? { b: _w((slotProps, s0, i0) => { return { a: _t(slotProps.item), b: s0, c: i0 }; }, { name: 'default', vueId: '2a9ec0b0-0' }) } : {})
}`
)
})
@@ -67,9 +67,9 @@ describe('compiler: transform v-slot', () => {
test('v-for + scoped slots', () => {
assert(
`{{ slotProps.item }}`,
- `{{slotProps.a}}`,
+ `{{slotProps.a}}`,
`(_ctx, _cache) => {
- return { a: _f(_ctx.items, (item, k0, i0) => { return { a: _w((slotProps, s1, i1) => { return { a: _t(slotProps.item), b: s1 }; }, { name: 'default', vueId: '2a9ec0b0-0' + '-' + i0 }), b: '2a9ec0b0-0' + '-' + i0 }; }) }
+ return { a: _f(_ctx.items, (item, k0, i0) => { return { a: _w((slotProps, s1, i1) => { return { a: _t(slotProps.item), b: s1, c: i1 }; }, { name: 'default', vueId: '2a9ec0b0-0' + '-' + i0 }), b: '2a9ec0b0-0' + '-' + i0 }; }) }
}`
)
})
@@ -77,16 +77,16 @@ describe('compiler: transform v-slot', () => {
test('v-for + v-for + scoped slots', () => {
assert(
`{{ slotProps.item }}`,
- `{{slotProps.a}}`,
+ `{{slotProps.a}}`,
`(_ctx, _cache) => {
- return { a: _f(_ctx.items, (item, k0, i0) => { return { a: _f(item.list, (item1, k1, i1) => { return { a: _w((slotProps, s2, i2) => { return { a: _t(slotProps.item), b: s2 }; }, { name: 'default', vueId: '2a9ec0b0-0' + '-' + i0 + '-' + i1 }), b: item1, c: '2a9ec0b0-0' + '-' + i0 + '-' + i1 }; }) }; }) }
+ return { a: _f(_ctx.items, (item, k0, i0) => { return { a: _f(item.list, (item1, k1, i1) => { return { a: _w((slotProps, s2, i2) => { return { a: _t(slotProps.item), b: s2, c: i2 }; }, { name: 'default', vueId: '2a9ec0b0-0' + '-' + i0 + '-' + i1 }), b: item1, c: '2a9ec0b0-0' + '-' + i0 + '-' + i1 }; }) }; }) }
}`
)
})
test('old syntax', () => {
assert(
``,
- ``,
+ ``,
`(_ctx, _cache) => {
return {}
}`
diff --git a/packages/uni-mp-compiler/src/ast.ts b/packages/uni-mp-compiler/src/ast.ts
index 8ed984648af2d6f32da0ea716c3c7b3c50b87e68..b290040956853389361a4d9a2d557254707ac8d5 100644
--- a/packages/uni-mp-compiler/src/ast.ts
+++ b/packages/uni-mp-compiler/src/ast.ts
@@ -98,7 +98,7 @@ export function parseExpr(
ErrorCodes.X_INVALID_EXPRESSION,
node && node.loc,
undefined,
- e.message
+ '\n' + code + '\n' + e.message
)
)
}
diff --git a/packages/uni-mp-compiler/src/index.ts b/packages/uni-mp-compiler/src/index.ts
index 7b09d4d5369b06123f7d56b64483f120df91a9a0..ee08593313925e53f02474291e227eaf21e27a5a 100644
--- a/packages/uni-mp-compiler/src/index.ts
+++ b/packages/uni-mp-compiler/src/index.ts
@@ -21,6 +21,7 @@ export type {
} from '@vue/compiler-core'
export { genExpr } from './codegen'
+export { rewriteExpression } from './transforms/utils'
export { isForElementNode } from './transforms/vFor'
export { transformOn } from './transforms/vOn'
export { transformModel } from './transforms/vModel'
diff --git a/packages/uni-mp-compiler/src/template/codegen.ts b/packages/uni-mp-compiler/src/template/codegen.ts
index a946103923c7d97a9f010b5a8612b7dabc7cb864..dda1080d862d536c821bbdbb23995ff46115f1d0 100644
--- a/packages/uni-mp-compiler/src/template/codegen.ts
+++ b/packages/uni-mp-compiler/src/template/codegen.ts
@@ -123,7 +123,13 @@ function genVFor(
function genSlot(node: SlotOutletNode, context: TemplateCodegenContext) {
// 移除掉所有非name属性,即移除作用域插槽的绑定指令
- node.props = node.props.filter((prop) => prop.name === 'name')
+ node.props = node.props.filter((prop) => {
+ if (prop.type === NodeTypes.ATTRIBUTE) {
+ return prop.name === 'name'
+ } else if (prop.arg?.type === NodeTypes.SIMPLE_EXPRESSION) {
+ return prop.arg.content === 'name'
+ }
+ })
if (!node.children.length || context.slot.fallbackContent) {
// 无后备内容或支持后备内容
return genElement(node, context)
@@ -154,9 +160,16 @@ function genSlot(node: SlotOutletNode, context: TemplateCodegenContext) {
function genTemplate(node: TemplateNode, context: TemplateCodegenContext) {
const slotProp = node.props.find(
- (prop) => prop.type === NodeTypes.DIRECTIVE && prop.name === 'slot'
+ (prop) =>
+ prop.type === NodeTypes.DIRECTIVE &&
+ (prop.name === 'slot' ||
+ (prop.name === 'bind' &&
+ prop.arg?.type === NodeTypes.SIMPLE_EXPRESSION &&
+ prop.arg.content === 'slot'))
) as DirectiveNode | undefined
- if (slotProp && findSlotName(slotProp)) {
+
+ // 为 bind 时,通常是作用域插槽生成的 vSlot.ts:197 createBindDirectiveNode('slot',...)
+ if (slotProp && (slotProp.name === 'bind' || findSlotName(slotProp))) {
/**
* 仅百度、字节支持使用 block 作为命名插槽根节点
* 此处为了统一仅默认替换为view
@@ -213,7 +226,11 @@ function genElement(node: ElementNode, context: TemplateCodegenContext) {
let tag = node.tag
// =>
if (tag === 'template') {
- tag = 'block'
+ if (findProp(node, 'slot')) {
+ tag = 'view'
+ } else {
+ tag = 'block'
+ }
}
if (node.tagType === ElementTypes.COMPONENT) {
tag = hyphenate(tag)
diff --git a/packages/uni-mp-compiler/src/transforms/transformSlot.ts b/packages/uni-mp-compiler/src/transforms/transformSlot.ts
index 7d937f8d44117cdcb9414a3402039c4786c094f5..e4529f784556517033a51cdf325d28c79663507e 100644
--- a/packages/uni-mp-compiler/src/transforms/transformSlot.ts
+++ b/packages/uni-mp-compiler/src/transforms/transformSlot.ts
@@ -2,9 +2,11 @@ import {
AttributeNode,
createCompilerError,
createCompoundExpression,
+ createSimpleExpression,
DirectiveNode,
ErrorCodes,
ExpressionNode,
+ findProp,
isBindKey,
isStaticExp,
NodeTypes,
@@ -14,16 +16,21 @@ import {
import { camelize } from '@vue/shared'
import { RENDER_SLOT } from '../runtimeHelpers'
import { genExpr } from '../codegen'
-import { TransformContext } from '../transform'
+import { isVForScope, TransformContext } from '../transform'
import { processProps } from './transformElement'
import { rewriteExpression } from './utils'
+import {
+ createAttributeNode,
+ createBindDirectiveNode,
+} from '@dcloudio/uni-cli-shared'
export function rewriteSlot(node: SlotOutletNode, context: TransformContext) {
let slotName: string | ExpressionNode = `"default"`
let hasOtherDir = false
const nonNameProps: (AttributeNode | DirectiveNode)[] = []
- for (let i = 0; i < node.props.length; i++) {
- const p = node.props[i]
+ const { props } = node
+ for (let i = 0; i < props.length; i++) {
+ const p = props[i]
if (p.type === NodeTypes.ATTRIBUTE) {
if (p.value) {
if (p.name === 'name') {
@@ -65,12 +72,31 @@ export function rewriteSlot(node: SlotOutletNode, context: TransformContext) {
}
})
if (properties.length) {
+ const slotKey = parseScopedSlotKey(context)
+ const nameProps = findProp(node, 'name')
+ if (!nameProps) {
+ // 生成默认的 default 插槽名
+ if (slotKey) {
+ props.push(
+ createBindDirectiveNode(
+ 'name',
+ rewriteExpression(
+ createSimpleExpression('"default-"+' + slotKey),
+ context
+ ).content
+ )
+ )
+ } else {
+ props.push(createAttributeNode('name', 'default'))
+ }
+ }
rewriteExpression(
createCompoundExpression([
context.helperString(RENDER_SLOT) + '(',
slotName,
',',
`{${properties.join(',')}}`,
+ `${slotKey ? ',' + slotKey : ''}`,
')',
]),
context
@@ -79,6 +105,21 @@ export function rewriteSlot(node: SlotOutletNode, context: TransformContext) {
}
}
+function parseScopedSlotKey(context: TransformContext) {
+ let { currentScope } = context
+ const indexs: string[] = []
+ while (currentScope) {
+ if (isVForScope(currentScope)) {
+ indexs.push(currentScope.indexAlias)
+ }
+ currentScope = currentScope.parent!
+ }
+ const inFor = !!indexs.length
+ if (inFor) {
+ return indexs.reverse().join(`+'-'+`)
+ }
+}
+
function transformProperty(dir: DirectiveNode, context: TransformContext) {
if (!dir.arg || !dir.exp) {
return
diff --git a/packages/uni-mp-compiler/src/transforms/utils.ts b/packages/uni-mp-compiler/src/transforms/utils.ts
index ddfeaeee6e3580508f540bef4ec6c545a6aa2c57..ca74085c016bd06b8de7d7e4ba3e54143cc2d4da 100644
--- a/packages/uni-mp-compiler/src/transforms/utils.ts
+++ b/packages/uni-mp-compiler/src/transforms/utils.ts
@@ -19,6 +19,7 @@ import {
NodeTypes,
SimpleExpressionNode,
SourceLocation,
+ TransformContext as VueTransformContext,
} from '@vue/compiler-core'
import { walk, BaseNode } from 'estree-walker'
import { isUndefined, parseExpr } from '../ast'
@@ -96,9 +97,9 @@ export function rewriteExpressionWithoutProperty(
}
export function rewriteExpression(
node: ExpressionNode,
- context: TransformContext,
+ context: TransformContext | VueTransformContext,
babelNode?: Expression,
- scope: CodegenScope = context.currentScope,
+ scope: CodegenScope = (context as TransformContext).currentScope,
{ property, ignoreLiteral } = { property: true, ignoreLiteral: false }
) {
if (node.type === NodeTypes.SIMPLE_EXPRESSION && node.isStatic) {
@@ -106,7 +107,7 @@ export function rewriteExpression(
}
if (!babelNode) {
const code = genExpr(node)
- babelNode = parseExpr(code, context, node)
+ babelNode = parseExpr(code, context as TransformContext, node)
if (!babelNode) {
return createSimpleExpression(code)
}
@@ -119,8 +120,8 @@ export function rewriteExpression(
}
// wxs 等表达式
- if (context.filters?.length) {
- if (isReferencedByIds(babelNode, context.filters)) {
+ if ((context as TransformContext).filters?.length) {
+ if (isReferencedByIds(babelNode, (context as TransformContext).filters)) {
return createSimpleExpression(genExpr(node), false, node.loc)
}
}
diff --git a/packages/uni-mp-compiler/src/transforms/vSlot.ts b/packages/uni-mp-compiler/src/transforms/vSlot.ts
index 0ea125ea00613fc5ce143f601aec754b323f8f13..8db28e13adb57118dbc37b5eac7030f75466ab44 100644
--- a/packages/uni-mp-compiler/src/transforms/vSlot.ts
+++ b/packages/uni-mp-compiler/src/transforms/vSlot.ts
@@ -194,6 +194,10 @@ function createVForTemplate(
) {
const key = 's' + context.scopes.vFor
const keyProp: DirectiveNode = createBindDirectiveNode('key', key)
+ const slotProp: DirectiveNode = createBindDirectiveNode(
+ 'slot',
+ `i${context.scopes.vFor}`
+ )
const vForProp: DirectiveNode = {
type: NodeTypes.DIRECTIVE,
name: 'for',
@@ -212,7 +216,7 @@ function createVForTemplate(
tag: 'template',
type: NodeTypes.ELEMENT,
tagType: ElementTypes.TEMPLATE,
- props: [vForProp, keyProp],
+ props: [vForProp, keyProp, slotProp],
isSelfClosing: false,
codegenNode: undefined,
children: slotElement.children,
diff --git a/packages/uni-mp-toutiao/dist/uni.compiler.js b/packages/uni-mp-toutiao/dist/uni.compiler.js
index 29fe42059261f3d6521a5b2491d59ad16d715e89..295323f50e5380bd8c4ca03153b7c6af9af24669 100644
--- a/packages/uni-mp-toutiao/dist/uni.compiler.js
+++ b/packages/uni-mp-toutiao/dist/uni.compiler.js
@@ -2,6 +2,10 @@
var initMiniProgramPlugin = require('@dcloudio/uni-mp-vite');
var path = require('path');
+var uniShared = require('@dcloudio/uni-shared');
+var uniCliShared = require('@dcloudio/uni-cli-shared');
+var uniMpCompiler = require('@dcloudio/uni-mp-compiler');
+var compilerCore = require('@vue/compiler-core');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
@@ -29,7 +33,48 @@ var source = {
condition: condition
};
+function transformSwiper(node) {
+ if (node.type !== 1 /* ELEMENT */ || node.tag !== 'swiper') {
+ return;
+ }
+ const disableTouchProp = compilerCore.findProp(node, 'disable-touch', false, true);
+ if (!disableTouchProp) {
+ return;
+ }
+ const { props } = node;
+ if (disableTouchProp.type === 6 /* ATTRIBUTE */) {
+ // =>
+ props.splice(props.indexOf(disableTouchProp), 1, uniCliShared.createBindDirectiveNode('touchable', 'false'));
+ }
+ else {
+ if (disableTouchProp.exp) {
+ // =>
+ let touchable = '';
+ if (disableTouchProp.exp.type === 4 /* SIMPLE_EXPRESSION */) {
+ if (disableTouchProp.exp.content === 'true') {
+ touchable = 'false';
+ }
+ else if (disableTouchProp.exp.content === 'false') {
+ touchable = 'true';
+ }
+ }
+ props.splice(props.indexOf(disableTouchProp), 1, uniCliShared.createBindDirectiveNode('touchable', touchable || `!(${uniMpCompiler.genExpr(disableTouchProp.exp)})`));
+ }
+ }
+}
+
const projectConfigFilename = 'project.config.json';
+const nodeTransforms = [
+ uniCliShared.transformRef,
+ transformSwiper,
+ uniCliShared.transformMatchMedia,
+ uniCliShared.transformComponentLink,
+];
+const compilerOptions = {
+ isNativeTag: uniShared.isNativeTag,
+ isCustomElement: uniShared.isCustomElement,
+ nodeTransforms,
+};
const miniProgram = {
class: {
array: false,
@@ -72,7 +117,7 @@ const options = {
${filter.code}
`;
},
- }, extname: '.ttml' }),
+ }, extname: '.ttml', compilerOptions }),
style: {
extname: '.ttss',
},
diff --git a/packages/uni-mp-toutiao/src/compiler/options.ts b/packages/uni-mp-toutiao/src/compiler/options.ts
index 1a5766b714ce62ab1b45556f8088236f948edf06..8eff627a12c183b00fbf58967af7bcde96d382b3 100644
--- a/packages/uni-mp-toutiao/src/compiler/options.ts
+++ b/packages/uni-mp-toutiao/src/compiler/options.ts
@@ -74,6 +74,7 @@ ${filter.code}
},
},
extname: '.ttml',
+ compilerOptions,
},
style: {
extname: '.ttss',
diff --git a/packages/uni-mp-vue/dist/vue.runtime.esm.js b/packages/uni-mp-vue/dist/vue.runtime.esm.js
index 75c60a6926eb10faec6b267da96184068126aaab..2e0314d8f12a8452a5f1b822ca4c13ee588b6440 100644
--- a/packages/uni-mp-vue/dist/vue.runtime.esm.js
+++ b/packages/uni-mp-vue/dist/vue.runtime.esm.js
@@ -5016,7 +5016,7 @@ function vFor(source, renderItem) {
return ret;
}
-function renderSlot(name, props = {}) {
+function renderSlot(name, props = {}, key) {
const instance = getCurrentInstance();
const vueIds = instance.attrs.vI;
if (!vueIds) {
@@ -5025,14 +5025,14 @@ function renderSlot(name, props = {}) {
if (!instance.parent && !instance.isMounted) {
// 头条小程序首次 render 时,还没有 parent
onMounted(() => {
- renderSlot(name, props);
+ renderSlot(name, props, key);
}, instance);
return;
}
const invoker = findScopedSlotInvoker(vueIds.split(',')[0], instance);
// 可能不存在,因为插槽不是必需的
if (invoker) {
- invoker(name, props);
+ invoker(name, props, key);
}
}
function findScopedSlotInvoker(vueId, instance) {
@@ -5063,13 +5063,18 @@ function withScopedSlot(fn, { name, path, vueId, }) {
else {
invoker.slots[name].fn = fn;
}
- // 返回单元素数组,因为 scoped slot 被转换成了 for 循环
- return [invoker.slots[name].data];
+ return invoker.slots[name].data;
}
function createScopedSlotInvoker(instance) {
- const invoker = (slotName, args) => {
+ const invoker = (slotName, args, key) => {
const slot = invoker.slots[slotName];
- slot.data = slot.fn(args, 0, 0);
+ const hasKey = typeof key !== 'undefined';
+ key = (key || '0') + '';
+ if (!hasKey) {
+ // 循环第一个 slot 时,重置 data
+ slot.data = {};
+ }
+ slot.data[key] = slot.fn(args, key, slotName + (hasKey ? '-' + key : ''));
// TODO 简单的 forceUpdate,理论上,可以仅对 scoped slot 部分数据 diff 更新
instance.proxy.$forceUpdate();
};
@@ -5090,7 +5095,7 @@ function setupDevtoolsPlugin() {
const o = (value) => vOn(value);
const f = (source, renderItem) => vFor(source, renderItem);
-const r = (name, props) => renderSlot(name, props);
+const r = (name, props, key) => renderSlot(name, props, key);
const w = (fn, options) => withScopedSlot(fn, options);
const s = (value) => stringifyStyle(value);
const c = (str) => camelize(str);
diff --git a/packages/uni-mp-vue/src/helpers/index.ts b/packages/uni-mp-vue/src/helpers/index.ts
index 3ff82358543975b679b9c43920db7b1f46cc3592..20e197a173567e7bc478810b2397e30620251293 100644
--- a/packages/uni-mp-vue/src/helpers/index.ts
+++ b/packages/uni-mp-vue/src/helpers/index.ts
@@ -18,7 +18,8 @@ export const f: typeof vFor = (
source: any,
renderItem: (...args: any[]) => VForItem
) => vFor(source, renderItem)
-export const r: typeof renderSlot = (name, props) => renderSlot(name, props)
+export const r: typeof renderSlot = (name, props, key) =>
+ renderSlot(name, props, key)
export const w: typeof withScopedSlot = (fn, options) =>
withScopedSlot(fn, options)
export const s: typeof stringifyStyle = (value) => stringifyStyle(value)
diff --git a/packages/uni-mp-vue/src/helpers/renderSlot.ts b/packages/uni-mp-vue/src/helpers/renderSlot.ts
index 0b70c6acfd08c95a33d9fe41ec445ae4192cd545..811693530eaa90645bef9363f03f3cc9f06fbb76 100644
--- a/packages/uni-mp-vue/src/helpers/renderSlot.ts
+++ b/packages/uni-mp-vue/src/helpers/renderSlot.ts
@@ -2,7 +2,11 @@ import type { ComponentInternalInstance } from 'vue'
import type { ScopedSlotInvokers } from './withScopedSlot'
import { onMounted, getCurrentInstance } from 'vue'
-export function renderSlot(name: string, props: Data = {}) {
+export function renderSlot(
+ name: string,
+ props: Data = {},
+ key?: string | number
+) {
const instance = getCurrentInstance() as ComponentInternalInstance
const vueIds = instance.attrs.vI as string
if (!vueIds) {
@@ -11,14 +15,14 @@ export function renderSlot(name: string, props: Data = {}) {
if (!instance.parent && !instance.isMounted) {
// 头条小程序首次 render 时,还没有 parent
onMounted(() => {
- renderSlot(name, props)
+ renderSlot(name, props, key)
}, instance)
return
}
const invoker = findScopedSlotInvoker(vueIds.split(',')[0], instance)
// 可能不存在,因为插槽不是必需的
if (invoker) {
- invoker(name, props)
+ invoker(name, props, key)
}
}
diff --git a/packages/uni-mp-vue/src/helpers/withScopedSlot.ts b/packages/uni-mp-vue/src/helpers/withScopedSlot.ts
index d8f09803fcecdea8e33515d70c3348a2cb2ed145..713571f33139699baf82ba94f39b2d859c0f1fa9 100644
--- a/packages/uni-mp-vue/src/helpers/withScopedSlot.ts
+++ b/packages/uni-mp-vue/src/helpers/withScopedSlot.ts
@@ -7,16 +7,19 @@ export interface ScopedSlotInvokers {
}
interface ScopedSlotFn {
- (args: Data, key: number, index: number): Record
+ (args: Data, key: string, slotName: string): Record
path: string
}
+interface ScopedSlotData {
+ [key: string]: Data
+}
interface ScopedSlotInvoker {
- (slotName: string, args: Data): void
+ (slotName: string, args: Data, key?: string | number): void
slots: {
[slotName: string]: {
fn: ScopedSlotFn
- data: Data
+ data: ScopedSlotData
}
}
}
@@ -48,14 +51,23 @@ export function withScopedSlot(
} else {
invoker.slots[name].fn = fn
}
- // 返回单元素数组,因为 scoped slot 被转换成了 for 循环
- return [invoker.slots[name].data]
+ return invoker.slots[name].data
}
function createScopedSlotInvoker(instance: ComponentInternalInstance) {
- const invoker: ScopedSlotInvoker = (slotName: string, args: Data) => {
+ const invoker: ScopedSlotInvoker = (
+ slotName: string,
+ args: Data,
+ key?: string | number
+ ) => {
const slot = invoker.slots[slotName]
- slot.data = slot.fn(args, 0, 0)
+ const hasKey = typeof key !== 'undefined'
+ key = (key || '0') + ''
+ if (!hasKey) {
+ // 循环第一个 slot 时,重置 data
+ slot.data = {}
+ }
+ slot.data[key] = slot.fn(args, key, slotName + (hasKey ? '-' + key : ''))
// TODO 简单的 forceUpdate,理论上,可以仅对 scoped slot 部分数据 diff 更新
instance.proxy!.$forceUpdate()
}
diff --git a/packages/uni-mp-weixin/dist/uni.api.esm.js b/packages/uni-mp-weixin/dist/uni.api.esm.js
index ad6057f56b269831637341e8ede01021facf088e..3ec9f8470721d9cc08c973f3a6bcc5d7a8d2629a 100644
--- a/packages/uni-mp-weixin/dist/uni.api.esm.js
+++ b/packages/uni-mp-weixin/dist/uni.api.esm.js
@@ -833,16 +833,39 @@ const previewImage = {
},
};
+const mocks = ['__route__', '__wxExparserNodeId__', '__wxWebviewId__'];
+
const getProvider = initGetProvider({
oauth: ['weixin'],
share: ['weixin'],
payment: ['wxpay'],
push: ['weixin'],
-});
+});
+function initComponentMocks(component) {
+ const res = Object.create(null);
+ mocks.forEach((name) => {
+ res[name] = component[name];
+ });
+ return res;
+}
+/**
+ * 微信小程序内部会 Object.keys(vm),导致告警
+ * Avoid app logic that relies on enumerating keys on a component instance. The keys will be empty in production mode to avoid performance overhead.
+ * @returns
+ */
+function createSelectorQuery() {
+ const query = wx.createSelectorQuery();
+ const oldIn = query.in;
+ query.in = function newIn(component) {
+ return oldIn.call(this, initComponentMocks(component));
+ };
+ return query;
+}
var shims = /*#__PURE__*/Object.freeze({
__proto__: null,
- getProvider: getProvider
+ getProvider: getProvider,
+ createSelectorQuery: createSelectorQuery
});
var protocols = /*#__PURE__*/Object.freeze({
diff --git a/packages/uni-mp-weixin/src/api/shims.ts b/packages/uni-mp-weixin/src/api/shims.ts
index 7a7ef9de1837624d97c45d38e01cc0139145b15c..b818d27b1b1104e3f7663450e693006f8e9025b1 100644
--- a/packages/uni-mp-weixin/src/api/shims.ts
+++ b/packages/uni-mp-weixin/src/api/shims.ts
@@ -1,4 +1,5 @@
-import { initGetProvider } from '@dcloudio/uni-mp-core'
+import { initGetProvider, MPComponentInstance } from '@dcloudio/uni-mp-core'
+import { mocks } from '../runtime/parseOptions'
export const getProvider = initGetProvider({
oauth: ['weixin'],
@@ -6,3 +7,28 @@ export const getProvider = initGetProvider({
payment: ['wxpay'],
push: ['weixin'],
})
+
+function initComponentMocks(
+ component:
+ | WechatMiniprogram.Component.TrivialInstance
+ | WechatMiniprogram.Page.TrivialInstance
+) {
+ const res: MPComponentInstance = Object.create(null)
+ mocks.forEach((name) => {
+ res[name] = component[name]
+ })
+ return res
+}
+/**
+ * 微信小程序内部会 Object.keys(vm),导致告警
+ * Avoid app logic that relies on enumerating keys on a component instance. The keys will be empty in production mode to avoid performance overhead.
+ * @returns
+ */
+export function createSelectorQuery() {
+ const query = wx.createSelectorQuery()
+ const oldIn = query.in
+ query.in = function newIn(component) {
+ return oldIn.call(this, initComponentMocks(component))
+ }
+ return query
+}