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

wip(mp): v-for + scoped slot

上级 20e52bc2
......@@ -27,9 +27,9 @@ describe('mp-alipay: transform v-slot', () => {
)
assert(
`<unicloud-db v-slot:default="{data, loading, error, options}" collection=""><view v-if="error">{{error.message}}</view><view v-else></view></unicloud-db>`,
`<unicloud-db slot="default" collection="" v-i="2a9ec0b0-0" onVI="__l"><block a:for="{{a}}" a:for-item="v0" a:key="c"><view a:if="{{v0.a}}">{{v0.b}}</view><view a:else></view></block></unicloud-db>`,
`<unicloud-db slot="default" collection="" v-i="2a9ec0b0-0" onVI="__l"><view a:for="{{a}}" a:for-item="v0" a:key="c" slot="{{v0.d}}"><view a:if="{{v0.a}}">{{v0.b}}</view><view a:else></view></view></unicloud-db>`,
`(_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(
`<custom><template v-slot:default="slotProps"><view>{{ slotProps.item }}</view></template></custom>`,
`<custom v-i="2a9ec0b0-0" onVI="__l"><view slot="default"><block a:for="{{a}}" a:for-item="slotProps" a:key="b"><view>{{slotProps.a}}</view></block></view></custom>`,
`<custom v-i="2a9ec0b0-0" onVI="__l"><view slot="default"><view a:for="{{a}}" a:for-item="slotProps" a:key="b" slot="{{slotProps.c}}"><view>{{slotProps.a}}</view></view></view></custom>`,
`(_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(
`<custom><template v-slot:default="slotProps"><custom1><template v-slot:default="slotProps1">{{ slotProps.item }}{{ slotProps1.item }}</template></custom1></template></custom>`,
`<custom v-i="2a9ec0b0-0" onVI="__l"><view slot="default"><block a:for="{{a}}" a:for-item="slotProps" a:key="d"><custom1 v-i="{{slotProps.c}}" onVI="__l"><view slot="default"><block a:for="{{slotProps.a}}" a:for-item="slotProps1" a:key="b">{{slotProps.b}}{{slotProps1.a}}</block></view></custom1></block></view></custom>`,
`<custom v-i="2a9ec0b0-0" onVI="__l"><view slot="default"><view a:for="{{a}}" a:for-item="slotProps" a:key="d" slot="{{slotProps.e}}"><custom1 v-i="{{slotProps.c}}" onVI="__l"><view slot="default"><view a:for="{{slotProps.a}}" a:for-item="slotProps1" a:key="b" slot="{{slotProps1.c}}">{{slotProps.b}}{{slotProps1.a}}</view></view></custom1></view></view></custom>`,
`(_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(
`<custom><template v-if="ok" v-slot:default="slotProps"><view>{{ slotProps.item }}</view></template></custom>`,
`<custom v-i="2a9ec0b0-0" onVI="__l"><view a:if="{{a}}" slot="default"><block a:for="{{b}}" a:for-item="slotProps" a:key="b"><view>{{slotProps.a}}</view></block></view></custom>`,
`<custom v-i="2a9ec0b0-0" onVI="__l"><view a:if="{{a}}" slot="default"><view a:for="{{b}}" a:for-item="slotProps" a:key="b" slot="{{slotProps.c}}"><view>{{slotProps.a}}</view></view></view></custom>`,
`(_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(
`<custom v-for="item in items"><template v-slot:default="slotProps"><view>{{ slotProps.item }}</view></template></custom>`,
`<custom a:for="{{a}}" a:for-item="item" v-i="{{item.b}}" onVI="__l"><view slot="default"><block a:for="{{item.a}}" a:for-item="slotProps" a:key="b"><view>{{slotProps.a}}</view></block></view></custom>`,
`<custom a:for="{{a}}" a:for-item="item" v-i="{{item.b}}" onVI="__l"><view slot="default"><view a:for="{{item.a}}" a:for-item="slotProps" a:key="b" slot="{{slotProps.c}}"><view>{{slotProps.a}}</view></view></view></custom>`,
`(_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(
`<view v-for="item in items"><custom v-for="item1 in item.list" :item="item1"><template v-slot:default="slotProps"><view>{{ slotProps.item }}</view></template></custom></view>`,
`<view a:for="{{a}}" a:for-item="item"><custom a:for="{{item.a}}" a:for-item="item1" item="{{item1.b}}" v-i="{{item1.c}}" onVI="__l"><view slot="default"><block a:for="{{item1.a}}" a:for-item="slotProps" a:key="b"><view>{{slotProps.a}}</view></block></view></custom></view>`,
`<view a:for="{{a}}" a:for-item="item"><custom a:for="{{item.a}}" a:for-item="item1" item="{{item1.b}}" v-i="{{item1.c}}" onVI="__l"><view slot="default"><view a:for="{{item1.a}}" a:for-item="slotProps" a:key="b" slot="{{slotProps.c}}"><view>{{slotProps.a}}</view></view></view></custom></view>`,
`(_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(
`<template slot="left"/>`,
`<block slot="left"/>`,
`<view slot="left"/>`,
`(_ctx, _cache) => {
return {}
}`
......
......@@ -4,9 +4,9 @@ describe(`mp-baidu: transform v-for`, () => {
test(`with key`, () => {
assert(
`<view v-for="item in items" :key="item.id"/>`,
`<view s-for="a trackBy item.id" s-for-item="item"/>`,
`<view s-for="a trackBy item.a" s-for-item="item"/>`,
`(_ctx, _cache) => {
return { a: _f(_ctx.items, (item, k0, i0) => { return {}; }) }
return { a: _f(_ctx.items, (item, k0, i0) => { return { a: item.id }; }) }
}`
)
})
......
......@@ -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);
}
......
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)
}
......
......@@ -4,7 +4,7 @@ describe('compiler: transform scoped slots', () => {
test('basic', () => {
assert(
`<view><slot :item="item" :index="index"/></view>`,
`<view><slot/></view>`,
`<view><slot name="default"/></view>`,
`(_ctx, _cache) => {
return { a: _r("default", { item: _ctx.item, index: _ctx.index }) }
}`
......
......@@ -40,9 +40,18 @@ describe('compiler: transform slot', () => {
test('slot with v-for', () => {
assert(
`<slot v-for="(item,index) in items" :key="index"></slot>`,
`<slot wx:for="{{a}}" wx:for-item="item"></slot>`,
`<slot wx:for="{{a}}" wx:for-item="item" name="{{item.a}}"></slot>`,
`(_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(
`<view v-for="(item,index) in items" :key="index"><slot v-for="(item1,index1) in item.list" :key="index1"></slot></view>`,
`<view wx:for="{{a}}" wx:for-item="item" wx:key="b"><slot wx:for="{{item.a}}" wx:for-item="item1" name="{{item1.a}}"></slot></view>`,
`(_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 }; }) }
}`
)
})
......
// import { inspect } from './testUtils'
import { compile } from '../src'
import { compile } from '../src/index'
import { CompilerOptions } from '../src/options'
import { miniProgram } from './testUtils'
......@@ -21,28 +21,24 @@ function assert(
...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)
if (res.code === renderCode) {
console.log('success')
} else {
console.error('error')
console.error(renderCode)
}
}
describe('compiler', () => {
test('scope', () => {
assert(
`<view :number="20" :str="'str'" :boolean="true" :null="null" :undefined="undefined"/>`,
`<view number="{{20}}" str="{{'str'}}" boolean="{{true}}" null="{{null}}" undefined="{{undefined}}"/>`,
`(_ctx, _cache) => {
return {}
assert(
`<template slot="left"/>`,
`<slot wx:for="{{a}}" wx:for-item="item"></slot>`,
`(_ctx, _cache) => {
return { a: _f(_ctx.items, (item, index, i0) => { return { a: _r(\"default\", { key: index }) }; }) }
}`
)
})
})
)
......@@ -27,9 +27,9 @@ describe('compiler: transform v-slot', () => {
)
assert(
`<unicloud-db v-slot:default="{data, loading, error, options}" collection=""><view v-if="error">{{error.message}}</view><view v-else></view></unicloud-db>`,
`<unicloud-db v-s="{{['default']}}" slot="default" collection="" v-i="2a9ec0b0-0"><block wx:for="{{a}}" wx:for-item="v0" wx:key="c"><view wx:if="{{v0.a}}">{{v0.b}}</view><view wx:else></view></block></unicloud-db>`,
`<unicloud-db v-s="{{['default']}}" slot="default" collection="" v-i="2a9ec0b0-0"><view wx:for="{{a}}" wx:for-item="v0" wx:key="c" slot="{{v0.d}}"><view wx:if="{{v0.a}}">{{v0.b}}</view><view wx:else></view></view></unicloud-db>`,
`(_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(
`<custom><template v-slot:default="slotProps"><view>{{ slotProps.item }}</view></template></custom>`,
`<custom v-s="{{['default']}}" v-i="2a9ec0b0-0"><view slot="default"><block wx:for="{{a}}" wx:for-item="slotProps" wx:key="b"><view>{{slotProps.a}}</view></block></view></custom>`,
`<custom v-s="{{['default']}}" v-i="2a9ec0b0-0"><view slot="default"><view wx:for="{{a}}" wx:for-item="slotProps" wx:key="b" slot="{{slotProps.c}}"><view>{{slotProps.a}}</view></view></view></custom>`,
`(_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(
`<custom><template v-slot:default="slotProps"><custom1><template v-slot:default="slotProps1">{{ slotProps.item }}{{ slotProps1.item }}</template></custom1></template></custom>`,
`<custom v-s="{{['default']}}" v-i="2a9ec0b0-0"><view slot="default"><block wx:for="{{a}}" wx:for-item="slotProps" wx:key="d"><custom1 v-s="{{['default']}}" v-i="{{slotProps.c}}"><view slot="default"><block wx:for="{{slotProps.a}}" wx:for-item="slotProps1" wx:key="b">{{slotProps.b}}{{slotProps1.a}}</block></view></custom1></block></view></custom>`,
`<custom v-s="{{['default']}}" v-i="2a9ec0b0-0"><view slot="default"><view wx:for="{{a}}" wx:for-item="slotProps" wx:key="d" slot="{{slotProps.e}}"><custom1 v-s="{{['default']}}" v-i="{{slotProps.c}}"><view slot="default"><view wx:for="{{slotProps.a}}" wx:for-item="slotProps1" wx:key="b" slot="{{slotProps1.c}}">{{slotProps.b}}{{slotProps1.a}}</view></view></custom1></view></view></custom>`,
`(_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(
`<custom><template v-if="ok" v-slot:default="slotProps"><view>{{ slotProps.item }}</view></template></custom>`,
`<custom v-s="{{['default']}}" v-i="2a9ec0b0-0"><view wx:if="{{a}}" slot="default"><block wx:for="{{b}}" wx:for-item="slotProps" wx:key="b"><view>{{slotProps.a}}</view></block></view></custom>`,
`<custom v-s="{{['default']}}" v-i="2a9ec0b0-0"><view wx:if="{{a}}" slot="default"><view wx:for="{{b}}" wx:for-item="slotProps" wx:key="b" slot="{{slotProps.c}}"><view>{{slotProps.a}}</view></view></view></custom>`,
`(_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(
`<custom v-for="item in items"><template v-slot:default="slotProps"><view>{{ slotProps.item }}</view></template></custom>`,
`<custom wx:for="{{a}}" wx:for-item="item" v-s="{{['default']}}" v-i="{{item.b}}"><view slot="default"><block wx:for="{{item.a}}" wx:for-item="slotProps" wx:key="b"><view>{{slotProps.a}}</view></block></view></custom>`,
`<custom wx:for="{{a}}" wx:for-item="item" v-s="{{['default']}}" v-i="{{item.b}}"><view slot="default"><view wx:for="{{item.a}}" wx:for-item="slotProps" wx:key="b" slot="{{slotProps.c}}"><view>{{slotProps.a}}</view></view></view></custom>`,
`(_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(
`<view v-for="item in items"><custom v-for="item1 in item.list" :item="item1"><template v-slot:default="slotProps"><view>{{ slotProps.item }}</view></template></custom></view>`,
`<view wx:for="{{a}}" wx:for-item="item"><custom wx:for="{{item.a}}" wx:for-item="item1" v-s="{{['default']}}" item="{{item1.b}}" v-i="{{item1.c}}"><view slot="default"><block wx:for="{{item1.a}}" wx:for-item="slotProps" wx:key="b"><view>{{slotProps.a}}</view></block></view></custom></view>`,
`<view wx:for="{{a}}" wx:for-item="item"><custom wx:for="{{item.a}}" wx:for-item="item1" v-s="{{['default']}}" item="{{item1.b}}" v-i="{{item1.c}}"><view slot="default"><view wx:for="{{item1.a}}" wx:for-item="slotProps" wx:key="b" slot="{{slotProps.c}}"><view>{{slotProps.a}}</view></view></view></custom></view>`,
`(_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(
`<template slot="left"/>`,
`<block slot="left"/>`,
`<view slot="left"/>`,
`(_ctx, _cache) => {
return {}
}`
......
......@@ -98,7 +98,7 @@ export function parseExpr(
ErrorCodes.X_INVALID_EXPRESSION,
node && node.loc,
undefined,
e.message
'\n' + code + '\n' + e.message
)
)
}
......
......@@ -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'
......
......@@ -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
// <template slot="left"/> => <block slot="left"/>
if (tag === 'template') {
tag = 'block'
if (findProp(node, 'slot')) {
tag = 'view'
} else {
tag = 'block'
}
}
if (node.tagType === ElementTypes.COMPONENT) {
tag = hyphenate(tag)
......
......@@ -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
......
......@@ -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)
}
}
......
......@@ -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,
......
......@@ -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 */) {
// <swiper disable-touch/> => <swiper :touchable="false"/>
props.splice(props.indexOf(disableTouchProp), 1, uniCliShared.createBindDirectiveNode('touchable', 'false'));
}
else {
if (disableTouchProp.exp) {
// <swiper :disable-touch="true"/> => <swiper :touchable="!(true)"/>
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}
</sjs>`;
},
}, extname: '.ttml' }),
}, extname: '.ttml', compilerOptions }),
style: {
extname: '.ttss',
},
......
......@@ -74,6 +74,7 @@ ${filter.code}
},
},
extname: '.ttml',
compilerOptions,
},
style: {
extname: '.ttss',
......
......@@ -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);
......
......@@ -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)
......
......@@ -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)
}
}
......
......@@ -7,16 +7,19 @@ export interface ScopedSlotInvokers {
}
interface ScopedSlotFn {
(args: Data, key: number, index: number): Record<string, any>
(args: Data, key: string, slotName: string): Record<string, any>
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()
}
......
......@@ -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({
......
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
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册