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

wip(mp): v-for + scoped slot

上级 20e52bc2
...@@ -27,9 +27,9 @@ describe('mp-alipay: transform v-slot', () => { ...@@ -27,9 +27,9 @@ describe('mp-alipay: transform v-slot', () => {
) )
assert( 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-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) => { `(_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', () => { ...@@ -37,9 +37,9 @@ describe('mp-alipay: transform v-slot', () => {
test('scoped slots', () => { test('scoped slots', () => {
assert( assert(
`<custom><template v-slot:default="slotProps"><view>{{ slotProps.item }}</view></template></custom>`, `<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) => { `(_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', () => { ...@@ -47,9 +47,9 @@ describe('mp-alipay: transform v-slot', () => {
test('scoped slots + scoped slots', () => { test('scoped slots + scoped slots', () => {
assert( assert(
`<custom><template v-slot:default="slotProps"><custom1><template v-slot:default="slotProps1">{{ slotProps.item }}{{ slotProps1.item }}</template></custom1></template></custom>`, `<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) => { `(_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', () => { ...@@ -57,9 +57,9 @@ describe('mp-alipay: transform v-slot', () => {
test('v-if + scoped slots', () => { test('v-if + scoped slots', () => {
assert( assert(
`<custom><template v-if="ok" v-slot:default="slotProps"><view>{{ slotProps.item }}</view></template></custom>`, `<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) => { `(_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', () => { ...@@ -67,9 +67,9 @@ describe('mp-alipay: transform v-slot', () => {
test('v-for + scoped slots', () => { test('v-for + scoped slots', () => {
assert( assert(
`<custom v-for="item in items"><template v-slot:default="slotProps"><view>{{ slotProps.item }}</view></template></custom>`, `<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) => { `(_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', () => { ...@@ -77,16 +77,16 @@ describe('mp-alipay: transform v-slot', () => {
test('v-for + v-for + scoped slots', () => { test('v-for + v-for + scoped slots', () => {
assert( 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 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) => { `(_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', () => { test('old syntax', () => {
assert( assert(
`<template slot="left"/>`, `<template slot="left"/>`,
`<block slot="left"/>`, `<view slot="left"/>`,
`(_ctx, _cache) => { `(_ctx, _cache) => {
return {} return {}
}` }`
......
...@@ -4,9 +4,9 @@ describe(`mp-baidu: transform v-for`, () => { ...@@ -4,9 +4,9 @@ describe(`mp-baidu: transform v-for`, () => {
test(`with key`, () => { test(`with key`, () => {
assert( assert(
`<view v-for="item in items" :key="item.id"/>`, `<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) => { `(_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 = { ...@@ -37,7 +37,7 @@ var source = {
setting: setting setting: setting
}; };
const transformFor = (node) => { const transformFor = (node, context) => {
if (!uniMpCompiler.isForElementNode(node)) { if (!uniMpCompiler.isForElementNode(node)) {
return; return;
} }
...@@ -45,7 +45,7 @@ const transformFor = (node) => { ...@@ -45,7 +45,7 @@ const transformFor = (node) => {
if (keyProp) { if (keyProp) {
const { exp } = keyProp; const { exp } = keyProp;
if (exp) { if (exp) {
const key = uniMpCompiler.genExpr(exp); const key = uniMpCompiler.rewriteExpression(exp, context).content;
node.vFor.sourceCode = `${node.vFor.sourceAlias} trackBy ${key}`; node.vFor.sourceCode = `${node.vFor.sourceAlias} trackBy ${key}`;
node.props.splice(node.props.indexOf(keyProp), 1); node.props.splice(node.props.indexOf(keyProp), 1);
} }
......
import { import {
genExpr,
findProp, findProp,
isForElementNode, isForElementNode,
DirectiveNode, DirectiveNode,
NodeTransform, NodeTransform,
rewriteExpression,
} from '@dcloudio/uni-mp-compiler' } from '@dcloudio/uni-mp-compiler'
export const transformFor: NodeTransform = (node) => { export const transformFor: NodeTransform = (node, context) => {
if (!isForElementNode(node)) { if (!isForElementNode(node)) {
return return
} }
...@@ -14,7 +14,7 @@ export const transformFor: NodeTransform = (node) => { ...@@ -14,7 +14,7 @@ export const transformFor: NodeTransform = (node) => {
if (keyProp) { if (keyProp) {
const { exp } = keyProp as DirectiveNode const { exp } = keyProp as DirectiveNode
if (exp) { if (exp) {
const key = genExpr(exp) const key = rewriteExpression(exp, context).content
node.vFor.sourceCode = `${node.vFor.sourceAlias} trackBy ${key}` node.vFor.sourceCode = `${node.vFor.sourceAlias} trackBy ${key}`
node.props.splice(node.props.indexOf(keyProp), 1) node.props.splice(node.props.indexOf(keyProp), 1)
} }
......
...@@ -4,7 +4,7 @@ describe('compiler: transform scoped slots', () => { ...@@ -4,7 +4,7 @@ describe('compiler: transform scoped slots', () => {
test('basic', () => { test('basic', () => {
assert( assert(
`<view><slot :item="item" :index="index"/></view>`, `<view><slot :item="item" :index="index"/></view>`,
`<view><slot/></view>`, `<view><slot name="default"/></view>`,
`(_ctx, _cache) => { `(_ctx, _cache) => {
return { a: _r("default", { item: _ctx.item, index: _ctx.index }) } return { a: _r("default", { item: _ctx.item, index: _ctx.index }) }
}` }`
......
...@@ -40,9 +40,18 @@ describe('compiler: transform slot', () => { ...@@ -40,9 +40,18 @@ describe('compiler: transform slot', () => {
test('slot with v-for', () => { test('slot with v-for', () => {
assert( assert(
`<slot v-for="(item,index) in items" :key="index"></slot>`, `<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) => { `(_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 { inspect } from './testUtils'
import { compile } from '../src' import { compile } from '../src/index'
import { CompilerOptions } from '../src/options' import { CompilerOptions } from '../src/options'
import { miniProgram } from './testUtils' import { miniProgram } from './testUtils'
...@@ -21,28 +21,24 @@ function assert( ...@@ -21,28 +21,24 @@ function assert(
...miniProgram, ...miniProgram,
emitFile({ source }) { emitFile({ source }) {
console.log(source) console.log(source)
// expect(source).toBe(templateCode)
return '' return ''
}, },
}, },
...options, ...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) 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', () => { assert(
test('scope', () => { `<template slot="left"/>`,
assert( `<slot wx:for="{{a}}" wx:for-item="item"></slot>`,
`<view :number="20" :str="'str'" :boolean="true" :null="null" :undefined="undefined"/>`, `(_ctx, _cache) => {
`<view number="{{20}}" str="{{'str'}}" boolean="{{true}}" null="{{null}}" undefined="{{undefined}}"/>`, return { a: _f(_ctx.items, (item, index, i0) => { return { a: _r(\"default\", { key: index }) }; }) }
`(_ctx, _cache) => {
return {}
}` }`
) )
})
})
...@@ -27,9 +27,9 @@ describe('compiler: transform v-slot', () => { ...@@ -27,9 +27,9 @@ describe('compiler: transform v-slot', () => {
) )
assert( 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-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) => { `(_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', () => { ...@@ -37,9 +37,9 @@ describe('compiler: transform v-slot', () => {
test('scoped slots', () => { test('scoped slots', () => {
assert( assert(
`<custom><template v-slot:default="slotProps"><view>{{ slotProps.item }}</view></template></custom>`, `<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) => { `(_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', () => { ...@@ -47,9 +47,9 @@ describe('compiler: transform v-slot', () => {
test('scoped slots + scoped slots', () => { test('scoped slots + scoped slots', () => {
assert( assert(
`<custom><template v-slot:default="slotProps"><custom1><template v-slot:default="slotProps1">{{ slotProps.item }}{{ slotProps1.item }}</template></custom1></template></custom>`, `<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) => { `(_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', () => { ...@@ -57,9 +57,9 @@ describe('compiler: transform v-slot', () => {
test('v-if + scoped slots', () => { test('v-if + scoped slots', () => {
assert( assert(
`<custom><template v-if="ok" v-slot:default="slotProps"><view>{{ slotProps.item }}</view></template></custom>`, `<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) => { `(_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', () => { ...@@ -67,9 +67,9 @@ describe('compiler: transform v-slot', () => {
test('v-for + scoped slots', () => { test('v-for + scoped slots', () => {
assert( assert(
`<custom v-for="item in items"><template v-slot:default="slotProps"><view>{{ slotProps.item }}</view></template></custom>`, `<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) => { `(_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', () => { ...@@ -77,16 +77,16 @@ describe('compiler: transform v-slot', () => {
test('v-for + v-for + scoped slots', () => { test('v-for + v-for + scoped slots', () => {
assert( 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 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) => { `(_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', () => { test('old syntax', () => {
assert( assert(
`<template slot="left"/>`, `<template slot="left"/>`,
`<block slot="left"/>`, `<view slot="left"/>`,
`(_ctx, _cache) => { `(_ctx, _cache) => {
return {} return {}
}` }`
......
...@@ -98,7 +98,7 @@ export function parseExpr( ...@@ -98,7 +98,7 @@ export function parseExpr(
ErrorCodes.X_INVALID_EXPRESSION, ErrorCodes.X_INVALID_EXPRESSION,
node && node.loc, node && node.loc,
undefined, undefined,
e.message '\n' + code + '\n' + e.message
) )
) )
} }
......
...@@ -21,6 +21,7 @@ export type { ...@@ -21,6 +21,7 @@ export type {
} from '@vue/compiler-core' } from '@vue/compiler-core'
export { genExpr } from './codegen' export { genExpr } from './codegen'
export { rewriteExpression } from './transforms/utils'
export { isForElementNode } from './transforms/vFor' export { isForElementNode } from './transforms/vFor'
export { transformOn } from './transforms/vOn' export { transformOn } from './transforms/vOn'
export { transformModel } from './transforms/vModel' export { transformModel } from './transforms/vModel'
......
...@@ -123,7 +123,13 @@ function genVFor( ...@@ -123,7 +123,13 @@ function genVFor(
function genSlot(node: SlotOutletNode, context: TemplateCodegenContext) { function genSlot(node: SlotOutletNode, context: TemplateCodegenContext) {
// 移除掉所有非name属性,即移除作用域插槽的绑定指令 // 移除掉所有非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) { if (!node.children.length || context.slot.fallbackContent) {
// 无后备内容或支持后备内容 // 无后备内容或支持后备内容
return genElement(node, context) return genElement(node, context)
...@@ -154,9 +160,16 @@ function genSlot(node: SlotOutletNode, context: TemplateCodegenContext) { ...@@ -154,9 +160,16 @@ function genSlot(node: SlotOutletNode, context: TemplateCodegenContext) {
function genTemplate(node: TemplateNode, context: TemplateCodegenContext) { function genTemplate(node: TemplateNode, context: TemplateCodegenContext) {
const slotProp = node.props.find( 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 ) as DirectiveNode | undefined
if (slotProp && findSlotName(slotProp)) {
// 为 bind 时,通常是作用域插槽生成的 vSlot.ts:197 createBindDirectiveNode('slot',...)
if (slotProp && (slotProp.name === 'bind' || findSlotName(slotProp))) {
/** /**
* 仅百度、字节支持使用 block 作为命名插槽根节点 * 仅百度、字节支持使用 block 作为命名插槽根节点
* 此处为了统一仅默认替换为view * 此处为了统一仅默认替换为view
...@@ -213,7 +226,11 @@ function genElement(node: ElementNode, context: TemplateCodegenContext) { ...@@ -213,7 +226,11 @@ function genElement(node: ElementNode, context: TemplateCodegenContext) {
let tag = node.tag let tag = node.tag
// <template slot="left"/> => <block slot="left"/> // <template slot="left"/> => <block slot="left"/>
if (tag === 'template') { if (tag === 'template') {
tag = 'block' if (findProp(node, 'slot')) {
tag = 'view'
} else {
tag = 'block'
}
} }
if (node.tagType === ElementTypes.COMPONENT) { if (node.tagType === ElementTypes.COMPONENT) {
tag = hyphenate(tag) tag = hyphenate(tag)
......
...@@ -2,9 +2,11 @@ import { ...@@ -2,9 +2,11 @@ import {
AttributeNode, AttributeNode,
createCompilerError, createCompilerError,
createCompoundExpression, createCompoundExpression,
createSimpleExpression,
DirectiveNode, DirectiveNode,
ErrorCodes, ErrorCodes,
ExpressionNode, ExpressionNode,
findProp,
isBindKey, isBindKey,
isStaticExp, isStaticExp,
NodeTypes, NodeTypes,
...@@ -14,16 +16,21 @@ import { ...@@ -14,16 +16,21 @@ import {
import { camelize } from '@vue/shared' import { camelize } from '@vue/shared'
import { RENDER_SLOT } from '../runtimeHelpers' import { RENDER_SLOT } from '../runtimeHelpers'
import { genExpr } from '../codegen' import { genExpr } from '../codegen'
import { TransformContext } from '../transform' import { isVForScope, TransformContext } from '../transform'
import { processProps } from './transformElement' import { processProps } from './transformElement'
import { rewriteExpression } from './utils' import { rewriteExpression } from './utils'
import {
createAttributeNode,
createBindDirectiveNode,
} from '@dcloudio/uni-cli-shared'
export function rewriteSlot(node: SlotOutletNode, context: TransformContext) { export function rewriteSlot(node: SlotOutletNode, context: TransformContext) {
let slotName: string | ExpressionNode = `"default"` let slotName: string | ExpressionNode = `"default"`
let hasOtherDir = false let hasOtherDir = false
const nonNameProps: (AttributeNode | DirectiveNode)[] = [] const nonNameProps: (AttributeNode | DirectiveNode)[] = []
for (let i = 0; i < node.props.length; i++) { const { props } = node
const p = node.props[i] for (let i = 0; i < props.length; i++) {
const p = props[i]
if (p.type === NodeTypes.ATTRIBUTE) { if (p.type === NodeTypes.ATTRIBUTE) {
if (p.value) { if (p.value) {
if (p.name === 'name') { if (p.name === 'name') {
...@@ -65,12 +72,31 @@ export function rewriteSlot(node: SlotOutletNode, context: TransformContext) { ...@@ -65,12 +72,31 @@ export function rewriteSlot(node: SlotOutletNode, context: TransformContext) {
} }
}) })
if (properties.length) { 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( rewriteExpression(
createCompoundExpression([ createCompoundExpression([
context.helperString(RENDER_SLOT) + '(', context.helperString(RENDER_SLOT) + '(',
slotName, slotName,
',', ',',
`{${properties.join(',')}}`, `{${properties.join(',')}}`,
`${slotKey ? ',' + slotKey : ''}`,
')', ')',
]), ]),
context context
...@@ -79,6 +105,21 @@ export function rewriteSlot(node: SlotOutletNode, context: TransformContext) { ...@@ -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) { function transformProperty(dir: DirectiveNode, context: TransformContext) {
if (!dir.arg || !dir.exp) { if (!dir.arg || !dir.exp) {
return return
......
...@@ -19,6 +19,7 @@ import { ...@@ -19,6 +19,7 @@ import {
NodeTypes, NodeTypes,
SimpleExpressionNode, SimpleExpressionNode,
SourceLocation, SourceLocation,
TransformContext as VueTransformContext,
} from '@vue/compiler-core' } from '@vue/compiler-core'
import { walk, BaseNode } from 'estree-walker' import { walk, BaseNode } from 'estree-walker'
import { isUndefined, parseExpr } from '../ast' import { isUndefined, parseExpr } from '../ast'
...@@ -96,9 +97,9 @@ export function rewriteExpressionWithoutProperty( ...@@ -96,9 +97,9 @@ export function rewriteExpressionWithoutProperty(
} }
export function rewriteExpression( export function rewriteExpression(
node: ExpressionNode, node: ExpressionNode,
context: TransformContext, context: TransformContext | VueTransformContext,
babelNode?: Expression, babelNode?: Expression,
scope: CodegenScope = context.currentScope, scope: CodegenScope = (context as TransformContext).currentScope,
{ property, ignoreLiteral } = { property: true, ignoreLiteral: false } { property, ignoreLiteral } = { property: true, ignoreLiteral: false }
) { ) {
if (node.type === NodeTypes.SIMPLE_EXPRESSION && node.isStatic) { if (node.type === NodeTypes.SIMPLE_EXPRESSION && node.isStatic) {
...@@ -106,7 +107,7 @@ export function rewriteExpression( ...@@ -106,7 +107,7 @@ export function rewriteExpression(
} }
if (!babelNode) { if (!babelNode) {
const code = genExpr(node) const code = genExpr(node)
babelNode = parseExpr(code, context, node) babelNode = parseExpr(code, context as TransformContext, node)
if (!babelNode) { if (!babelNode) {
return createSimpleExpression(code) return createSimpleExpression(code)
} }
...@@ -119,8 +120,8 @@ export function rewriteExpression( ...@@ -119,8 +120,8 @@ export function rewriteExpression(
} }
// wxs 等表达式 // wxs 等表达式
if (context.filters?.length) { if ((context as TransformContext).filters?.length) {
if (isReferencedByIds(babelNode, context.filters)) { if (isReferencedByIds(babelNode, (context as TransformContext).filters)) {
return createSimpleExpression(genExpr(node), false, node.loc) return createSimpleExpression(genExpr(node), false, node.loc)
} }
} }
......
...@@ -194,6 +194,10 @@ function createVForTemplate( ...@@ -194,6 +194,10 @@ function createVForTemplate(
) { ) {
const key = 's' + context.scopes.vFor const key = 's' + context.scopes.vFor
const keyProp: DirectiveNode = createBindDirectiveNode('key', key) const keyProp: DirectiveNode = createBindDirectiveNode('key', key)
const slotProp: DirectiveNode = createBindDirectiveNode(
'slot',
`i${context.scopes.vFor}`
)
const vForProp: DirectiveNode = { const vForProp: DirectiveNode = {
type: NodeTypes.DIRECTIVE, type: NodeTypes.DIRECTIVE,
name: 'for', name: 'for',
...@@ -212,7 +216,7 @@ function createVForTemplate( ...@@ -212,7 +216,7 @@ function createVForTemplate(
tag: 'template', tag: 'template',
type: NodeTypes.ELEMENT, type: NodeTypes.ELEMENT,
tagType: ElementTypes.TEMPLATE, tagType: ElementTypes.TEMPLATE,
props: [vForProp, keyProp], props: [vForProp, keyProp, slotProp],
isSelfClosing: false, isSelfClosing: false,
codegenNode: undefined, codegenNode: undefined,
children: slotElement.children, children: slotElement.children,
......
...@@ -2,6 +2,10 @@ ...@@ -2,6 +2,10 @@
var initMiniProgramPlugin = require('@dcloudio/uni-mp-vite'); var initMiniProgramPlugin = require('@dcloudio/uni-mp-vite');
var path = require('path'); 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 }; } function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
...@@ -29,7 +33,48 @@ var source = { ...@@ -29,7 +33,48 @@ var source = {
condition: condition 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 projectConfigFilename = 'project.config.json';
const nodeTransforms = [
uniCliShared.transformRef,
transformSwiper,
uniCliShared.transformMatchMedia,
uniCliShared.transformComponentLink,
];
const compilerOptions = {
isNativeTag: uniShared.isNativeTag,
isCustomElement: uniShared.isCustomElement,
nodeTransforms,
};
const miniProgram = { const miniProgram = {
class: { class: {
array: false, array: false,
...@@ -72,7 +117,7 @@ const options = { ...@@ -72,7 +117,7 @@ const options = {
${filter.code} ${filter.code}
</sjs>`; </sjs>`;
}, },
}, extname: '.ttml' }), }, extname: '.ttml', compilerOptions }),
style: { style: {
extname: '.ttss', extname: '.ttss',
}, },
......
...@@ -74,6 +74,7 @@ ${filter.code} ...@@ -74,6 +74,7 @@ ${filter.code}
}, },
}, },
extname: '.ttml', extname: '.ttml',
compilerOptions,
}, },
style: { style: {
extname: '.ttss', extname: '.ttss',
......
...@@ -5016,7 +5016,7 @@ function vFor(source, renderItem) { ...@@ -5016,7 +5016,7 @@ function vFor(source, renderItem) {
return ret; return ret;
} }
function renderSlot(name, props = {}) { function renderSlot(name, props = {}, key) {
const instance = getCurrentInstance(); const instance = getCurrentInstance();
const vueIds = instance.attrs.vI; const vueIds = instance.attrs.vI;
if (!vueIds) { if (!vueIds) {
...@@ -5025,14 +5025,14 @@ function renderSlot(name, props = {}) { ...@@ -5025,14 +5025,14 @@ function renderSlot(name, props = {}) {
if (!instance.parent && !instance.isMounted) { if (!instance.parent && !instance.isMounted) {
// 头条小程序首次 render 时,还没有 parent // 头条小程序首次 render 时,还没有 parent
onMounted(() => { onMounted(() => {
renderSlot(name, props); renderSlot(name, props, key);
}, instance); }, instance);
return; return;
} }
const invoker = findScopedSlotInvoker(vueIds.split(',')[0], instance); const invoker = findScopedSlotInvoker(vueIds.split(',')[0], instance);
// 可能不存在,因为插槽不是必需的 // 可能不存在,因为插槽不是必需的
if (invoker) { if (invoker) {
invoker(name, props); invoker(name, props, key);
} }
} }
function findScopedSlotInvoker(vueId, instance) { function findScopedSlotInvoker(vueId, instance) {
...@@ -5063,13 +5063,18 @@ function withScopedSlot(fn, { name, path, vueId, }) { ...@@ -5063,13 +5063,18 @@ function withScopedSlot(fn, { name, path, vueId, }) {
else { else {
invoker.slots[name].fn = fn; invoker.slots[name].fn = fn;
} }
// 返回单元素数组,因为 scoped slot 被转换成了 for 循环 return invoker.slots[name].data;
return [invoker.slots[name].data];
} }
function createScopedSlotInvoker(instance) { function createScopedSlotInvoker(instance) {
const invoker = (slotName, args) => { const invoker = (slotName, args, key) => {
const slot = invoker.slots[slotName]; 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 更新 // TODO 简单的 forceUpdate,理论上,可以仅对 scoped slot 部分数据 diff 更新
instance.proxy.$forceUpdate(); instance.proxy.$forceUpdate();
}; };
...@@ -5090,7 +5095,7 @@ function setupDevtoolsPlugin() { ...@@ -5090,7 +5095,7 @@ function setupDevtoolsPlugin() {
const o = (value) => vOn(value); const o = (value) => vOn(value);
const f = (source, renderItem) => vFor(source, renderItem); 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 w = (fn, options) => withScopedSlot(fn, options);
const s = (value) => stringifyStyle(value); const s = (value) => stringifyStyle(value);
const c = (str) => camelize(str); const c = (str) => camelize(str);
......
...@@ -18,7 +18,8 @@ export const f: typeof vFor = ( ...@@ -18,7 +18,8 @@ export const f: typeof vFor = (
source: any, source: any,
renderItem: (...args: any[]) => VForItem renderItem: (...args: any[]) => VForItem
) => vFor(source, renderItem) ) => 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) => export const w: typeof withScopedSlot = (fn, options) =>
withScopedSlot(fn, options) withScopedSlot(fn, options)
export const s: typeof stringifyStyle = (value) => stringifyStyle(value) export const s: typeof stringifyStyle = (value) => stringifyStyle(value)
......
...@@ -2,7 +2,11 @@ import type { ComponentInternalInstance } from 'vue' ...@@ -2,7 +2,11 @@ import type { ComponentInternalInstance } from 'vue'
import type { ScopedSlotInvokers } from './withScopedSlot' import type { ScopedSlotInvokers } from './withScopedSlot'
import { onMounted, getCurrentInstance } from 'vue' 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 instance = getCurrentInstance() as ComponentInternalInstance
const vueIds = instance.attrs.vI as string const vueIds = instance.attrs.vI as string
if (!vueIds) { if (!vueIds) {
...@@ -11,14 +15,14 @@ export function renderSlot(name: string, props: Data = {}) { ...@@ -11,14 +15,14 @@ export function renderSlot(name: string, props: Data = {}) {
if (!instance.parent && !instance.isMounted) { if (!instance.parent && !instance.isMounted) {
// 头条小程序首次 render 时,还没有 parent // 头条小程序首次 render 时,还没有 parent
onMounted(() => { onMounted(() => {
renderSlot(name, props) renderSlot(name, props, key)
}, instance) }, instance)
return return
} }
const invoker = findScopedSlotInvoker(vueIds.split(',')[0], instance) const invoker = findScopedSlotInvoker(vueIds.split(',')[0], instance)
// 可能不存在,因为插槽不是必需的 // 可能不存在,因为插槽不是必需的
if (invoker) { if (invoker) {
invoker(name, props) invoker(name, props, key)
} }
} }
......
...@@ -7,16 +7,19 @@ export interface ScopedSlotInvokers { ...@@ -7,16 +7,19 @@ export interface ScopedSlotInvokers {
} }
interface ScopedSlotFn { interface ScopedSlotFn {
(args: Data, key: number, index: number): Record<string, any> (args: Data, key: string, slotName: string): Record<string, any>
path: string path: string
} }
interface ScopedSlotData {
[key: string]: Data
}
interface ScopedSlotInvoker { interface ScopedSlotInvoker {
(slotName: string, args: Data): void (slotName: string, args: Data, key?: string | number): void
slots: { slots: {
[slotName: string]: { [slotName: string]: {
fn: ScopedSlotFn fn: ScopedSlotFn
data: Data data: ScopedSlotData
} }
} }
} }
...@@ -48,14 +51,23 @@ export function withScopedSlot( ...@@ -48,14 +51,23 @@ export function withScopedSlot(
} else { } else {
invoker.slots[name].fn = fn invoker.slots[name].fn = fn
} }
// 返回单元素数组,因为 scoped slot 被转换成了 for 循环 return invoker.slots[name].data
return [invoker.slots[name].data]
} }
function createScopedSlotInvoker(instance: ComponentInternalInstance) { 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] 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 更新 // TODO 简单的 forceUpdate,理论上,可以仅对 scoped slot 部分数据 diff 更新
instance.proxy!.$forceUpdate() instance.proxy!.$forceUpdate()
} }
......
...@@ -833,16 +833,39 @@ const previewImage = { ...@@ -833,16 +833,39 @@ const previewImage = {
}, },
}; };
const mocks = ['__route__', '__wxExparserNodeId__', '__wxWebviewId__'];
const getProvider = initGetProvider({ const getProvider = initGetProvider({
oauth: ['weixin'], oauth: ['weixin'],
share: ['weixin'], share: ['weixin'],
payment: ['wxpay'], payment: ['wxpay'],
push: ['weixin'], 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({ var shims = /*#__PURE__*/Object.freeze({
__proto__: null, __proto__: null,
getProvider: getProvider getProvider: getProvider,
createSelectorQuery: createSelectorQuery
}); });
var protocols = /*#__PURE__*/Object.freeze({ 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({ export const getProvider = initGetProvider({
oauth: ['weixin'], oauth: ['weixin'],
...@@ -6,3 +7,28 @@ export const getProvider = initGetProvider({ ...@@ -6,3 +7,28 @@ export const getProvider = initGetProvider({
payment: ['wxpay'], payment: ['wxpay'],
push: ['weixin'], 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.
先完成此消息的编辑!
想要评论请 注册