diff --git a/packages/uni-template-compiler/__tests__/compiler-mp-weixin.spec.js b/packages/uni-template-compiler/__tests__/compiler-mp-weixin.spec.js
index b60fcb8de74b0321b0c417eff551ca5aef648d2f..efa48612dac830a7185da76f0c784b2316f40554 100644
--- a/packages/uni-template-compiler/__tests__/compiler-mp-weixin.spec.js
+++ b/packages/uni-template-compiler/__tests__/compiler-mp-weixin.spec.js
@@ -82,6 +82,41 @@ describe('mp:compiler-mp-weixin', () => {
)
})
+ it('generate scoped slot with filter', () => {
+ assertCodegen(
+ '{{getValue(item)}}',
+ '{{$root.m1}}',
+ 'with(this){var m0=$hasScopedSlotsParams("551070e6-1");var m1=m0?getValue($getScopedSlotsParams("551070e6-1","default","item")):null;$mp.data=Object.assign({},{$root:{m0:m0,m1:m1}})}',
+ {
+ betterScopedSlots: true
+ }
+ )
+ assertCodegen(
+ '{{getValue(item.text)}}',
+ '{{$root.m1}}',
+ 'with(this){var m0=$hasScopedSlotsParams("551070e6-1");var m1=m0?getValue($getScopedSlotsParams("551070e6-1","default").text):null;$mp.data=Object.assign({},{$root:{m0:m0,m1:m1}})}',
+ {
+ betterScopedSlots: true
+ }
+ )
+ assertCodegen(
+ '',
+ '',
+ 'with(this){$setScopedSlotsParams("default",{"item":item})}',
+ {
+ betterScopedSlots: true
+ }
+ )
+ assertCodegen(
+ '',
+ '',
+ 'with(this){$setScopedSlotsParams("default",object)}',
+ {
+ betterScopedSlots: true
+ }
+ )
+ })
+
it('generate scoped slot', () => {
assertCodegen(
'',
@@ -158,4 +193,4 @@ describe('mp:compiler-mp-weixin', () => {
'hello world'
)
})
-})
+})
diff --git a/packages/uni-template-compiler/lib/constants.js b/packages/uni-template-compiler/lib/constants.js
index 9c68613a9d6ab982cd9943038582f368ec54f831..af0ea8ca2eaf74fd42d77df105d5a8313aedc318 100644
--- a/packages/uni-template-compiler/lib/constants.js
+++ b/packages/uni-template-compiler/lib/constants.js
@@ -106,6 +106,9 @@ module.exports = {
METHOD_TO_STRING,
METHOD_RENDER_LIST,
METHOD_RESOLVE_FILTER,
+ METHOD_RENDER_SLOT,
+ METHOD_CREATE_EMPTY_VNODE,
+ METHOD_RESOLVE_SCOPED_SLOTS,
PREFIX_GLOBAL: 'g',
PREFIX_ATTR: 'a',
PREFIX_METHOD: 'm',
diff --git a/packages/uni-template-compiler/lib/script/traverse/index.js b/packages/uni-template-compiler/lib/script/traverse/index.js
index 64325f4b97eef4f3bcd3e7e10d68fb1d0b7e6c05..c293ca116e86808a17ef7e9c7d50d6a3ff1e4814 100644
--- a/packages/uni-template-compiler/lib/script/traverse/index.js
+++ b/packages/uni-template-compiler/lib/script/traverse/index.js
@@ -90,6 +90,7 @@ module.exports = function traverse (ast, state) {
const blockStatementBody = []
const objectPropertyArray = []
const initExpressionStatementArray = []
+ const renderSlotStatementArray = []
// TODO 待重构,至少 filter,method 等实现方式要调整
babelTraverse(ast, visitor, undefined, {
scoped: [],
@@ -100,7 +101,8 @@ module.exports = function traverse (ast, state) {
identifierArray: identifierArray,
propertyArray: objectPropertyArray,
declarationArray: blockStatementBody,
- initExpressionStatementArray: initExpressionStatementArray
+ initExpressionStatementArray: initExpressionStatementArray,
+ renderSlotStatementArray
})
if (initExpressionStatementArray.length) {
@@ -111,10 +113,14 @@ module.exports = function traverse (ast, state) {
blockStatementBody.push(getDataExpressionStatement(objectPropertyArray))
}
+ if (renderSlotStatementArray.length) {
+ blockStatementBody.push(...renderSlotStatementArray)
+ }
+
reIdentifier(identifierArray)
return t.withStatement(
t.thisExpression(),
t.blockStatement(blockStatementBody)
)
-}
+}
diff --git a/packages/uni-template-compiler/lib/script/traverse/render-slot.js b/packages/uni-template-compiler/lib/script/traverse/render-slot.js
new file mode 100644
index 0000000000000000000000000000000000000000..d02dcce3a5d7f8eb03211060b1b97e826b462226
--- /dev/null
+++ b/packages/uni-template-compiler/lib/script/traverse/render-slot.js
@@ -0,0 +1,34 @@
+const t = require('@babel/types')
+
+module.exports = function getRenderSlot (path, state) {
+ const name = path.get('arguments.0')
+ const arg2 = path.get('arguments.2')
+ const arg3 = path.get('arguments.3')
+ let valueNode
+ if (arg3) {
+ // v-bind:object
+ valueNode = arg3.node
+ } else if (arg2 && !arg2.isNullLiteral()) {
+ if (arg2.isObjectExpression()) {
+ const propertiesPath = arg2.get('properties')
+ const oldProperties = []
+ const newProperties = []
+ propertiesPath.forEach(path => {
+ const properties = path.get('key').isStringLiteral({ value: 'SLOT_DEFAULT' }) ? oldProperties : newProperties
+ properties.push(path.node)
+ })
+ if (!newProperties.length) {
+ return
+ }
+ valueNode = t.objectExpression(newProperties)
+ arg2.replaceWith(t.objectExpression(oldProperties))
+ } else {
+ valueNode = arg2.node
+ }
+ }
+ if (valueNode) {
+ state.renderSlotStatementArray.push(t.expressionStatement(t.callExpression(t.identifier('$setScopedSlotsParams'), [t.stringLiteral(name.node.value), valueNode])))
+ }
+ // TODO 组件嵌套
+ path.skip()
+}
diff --git a/packages/uni-template-compiler/lib/script/traverse/resolve-scoped-slots.js b/packages/uni-template-compiler/lib/script/traverse/resolve-scoped-slots.js
new file mode 100644
index 0000000000000000000000000000000000000000..632e810a34c0fe71f30f4199729b9e191b55f8b0
--- /dev/null
+++ b/packages/uni-template-compiler/lib/script/traverse/resolve-scoped-slots.js
@@ -0,0 +1,54 @@
+const t = require('@babel/types')
+
+const {
+ METHOD_CREATE_EMPTY_VNODE
+} = require('../../constants')
+
+function replaceId (path, ids) {
+ let replaced
+ path.traverse({
+ noScope: true,
+ Identifier (path) {
+ const name = path.node.name
+ if (name in ids && path.key !== 'key' && (path.key !== 'property' || path.parent.computed)) {
+ path.replaceWith(ids[name])
+ replaced = true
+ }
+ }
+ })
+ return replaced
+}
+
+module.exports = function getResolveScopedSlots (parent, state) {
+ const properties = parent.get('arguments.0.elements.0.properties')
+ const fn = properties.find(path => path.get('key').isIdentifier({ name: 'fn' }))
+ const params = fn.get('value.params.0')
+ if (!params) {
+ return
+ }
+ const vueId = parent.parentPath.parentPath.get('properties').find(path => path.get('key').isIdentifier({ name: 'attrs' })).get('value').get('properties').find(path => path.get('key').isStringLiteral({ value: 'vue-id' })).get('value').node.value
+ const slot = properties.find(path => path.get('key').isIdentifier({ name: 'key' })).get('value').node.value
+ const ids = {}
+ function updateIds (vueId, slot, value, key) {
+ const array = [t.stringLiteral(vueId), t.stringLiteral(slot)]
+ if (key) {
+ array.push(t.stringLiteral(key))
+ }
+ ids[value] = t.callExpression(t.identifier('$getScopedSlotsParams'), array)
+ }
+ if (params.isObjectPattern()) {
+ params.get('properties').forEach(prop => {
+ updateIds(vueId, slot, prop.get('value').node.name, prop.get('key').node.name)
+ })
+ } else if (params.isIdentifier()) {
+ updateIds(vueId, slot, params.node.name)
+ }
+ const fnBody = fn.get('value.body')
+ if (replaceId(fnBody, ids)) {
+ const orgin = fnBody.get('body.0.argument')
+ const elements = orgin.get('elements')
+ const node = (elements.length === 1 ? elements[0] : orgin).node
+ const test = t.callExpression(t.identifier('$hasScopedSlotsParams'), [t.stringLiteral(vueId)])
+ orgin.replaceWith(t.arrayExpression([t.conditionalExpression(test, node, t.callExpression(t.identifier(METHOD_CREATE_EMPTY_VNODE), []))]))
+ }
+}
diff --git a/packages/uni-template-compiler/lib/script/traverse/visitor.js b/packages/uni-template-compiler/lib/script/traverse/visitor.js
index 15e7c14e6f9d84f1540c8e61dc8e5148858a7473..721cd334060aebe60236d4f02e10baa77090c30c 100644
--- a/packages/uni-template-compiler/lib/script/traverse/visitor.js
+++ b/packages/uni-template-compiler/lib/script/traverse/visitor.js
@@ -6,6 +6,8 @@ const {
METHOD_RENDER_LIST,
METHOD_BUILT_IN,
METHOD_RESOLVE_FILTER,
+ METHOD_RENDER_SLOT,
+ METHOD_RESOLVE_SCOPED_SLOTS,
IDENTIFIER_FILTER,
IDENTIFIER_METHOD,
IDENTIFIER_GLOBAL
@@ -26,6 +28,8 @@ const traverseData = require('./data')
const traverseRenderList = require('./render-list')
const getMemberExpr = require('./member-expr')
+const getRenderSlot = require('./render-slot')
+const getResolveScopedSlots = require('./resolve-scoped-slots')
function addStaticClass (path, staticClass) {
const dataPath = path.get('arguments.1')
@@ -217,6 +221,12 @@ module.exports = {
this
)
)
+ } else if (this.options.betterScopedSlots) {
+ if (methodName === METHOD_RESOLVE_SCOPED_SLOTS) {
+ getResolveScopedSlots(path, this)
+ } else if (methodName === METHOD_RENDER_SLOT) {
+ getRenderSlot(path, this)
+ }
}
break
}
diff --git a/packages/uni-template-compiler/lib/template/traverse.js b/packages/uni-template-compiler/lib/template/traverse.js
index 4d50cb5447ad0cf47b1625678d079bb5580efc90..fd700937121dc4a6358e0aae943677e71cbb0c66 100644
--- a/packages/uni-template-compiler/lib/template/traverse.js
+++ b/packages/uni-template-compiler/lib/template/traverse.js
@@ -304,7 +304,7 @@ function traverseRenderSlot (callExprNode, state) {
const slotName = callExprNode.arguments[0].value
let deleteSlotName = false // 标记是否组件 slot 手动指定了 name="default"
- if (callExprNode.arguments.length > 2) { // 作用域插槽
+ if (!state.options.betterScopedSlots && callExprNode.arguments.length > 2) { // 作用域插槽
const props = {}
callExprNode.arguments[2].properties.forEach(property => {
props[property.key.value] = genCode(property.value)
@@ -369,7 +369,7 @@ function traverseResolveScopedSlots (callExprNode, state) {
})
const slotName = keyProperty.value.value
const returnExprNodes = fnProperty.value.body.body[0].argument
- if (!proxyProperty) {
+ if (!state.options.betterScopedSlots && !proxyProperty) {
const resourcePath = state.options.resourcePath
const ownerName = path.basename(resourcePath, path.extname(resourcePath))
@@ -510,4 +510,4 @@ function traverseCreateTextVNode (callExprNode, state) {
function traverseCreateEmptyVNode (callExprNode, state) {
return ''
-}
+}
diff --git a/packages/vue-cli-plugin-uni/lib/env.js b/packages/vue-cli-plugin-uni/lib/env.js
index 9c87845314e624983842c74fccd245153b6b30a5..3b7c6d6cd2d083e76e79069fbd0104d896b36e43 100644
--- a/packages/vue-cli-plugin-uni/lib/env.js
+++ b/packages/vue-cli-plugin-uni/lib/env.js
@@ -258,6 +258,10 @@ if (platformOptions.usingComponents === true) {
}
}
+if (platformOptions.betterScopedSlots) {
+ process.env.BETTER_SCOPED_SLOTS = true
+}
+
if (
process.env.UNI_USING_COMPONENTS ||
process.env.UNI_PLATFORM === 'h5'
diff --git a/packages/webpack-uni-mp-loader/lib/template.js b/packages/webpack-uni-mp-loader/lib/template.js
index 6539148f7129fdbf3d2f7b47460bd93505ec345c..11ff5b8298fb6c98e04ea274c8f81f1b2ea36ef8 100644
--- a/packages/webpack-uni-mp-loader/lib/template.js
+++ b/packages/webpack-uni-mp-loader/lib/template.js
@@ -65,7 +65,8 @@ module.exports = function (content, map) {
const filterModules = parseFilterModules(params && params['filter-modules'])
Object.assign(vueLoaderOptions.options.compilerOptions, {
mp: {
- platform: process.env.UNI_PLATFORM
+ platform: process.env.UNI_PLATFORM,
+ betterScopedSlots: process.env.BETTER_SCOPED_SLOTS
},
filterModules,
filterTagName,
@@ -84,4 +85,4 @@ module.exports = function (content, map) {
throw new Error('vue-loader-options parse error')
}
this.callback(null, content, map)
-}
+}
diff --git a/src/platforms/mp-weixin/runtime/wrapper/app-base-parser.js b/src/platforms/mp-weixin/runtime/wrapper/app-base-parser.js
index 5d1ee7632e3cf305036df437568f82fc82f3a858..16883c507a0ddd2719d24aa27a037733d778e5da 100644
--- a/src/platforms/mp-weixin/runtime/wrapper/app-base-parser.js
+++ b/src/platforms/mp-weixin/runtime/wrapper/app-base-parser.js
@@ -41,11 +41,61 @@ function initEventChannel () {
}
}
+function initScopedSlotsParams () {
+ const center = {}
+ const parents = {}
+
+ Vue.prototype.$hasScopedSlotsParams = function (vueId) {
+ const has = center[vueId]
+ if (!has) {
+ parents[vueId] = this
+ this.$on('hook:destory', () => {
+ delete parents[vueId]
+ })
+ }
+ return has
+ }
+
+ Vue.prototype.$getScopedSlotsParams = function (vueId, name, key) {
+ const data = center[vueId]
+ if (data) {
+ const object = data[name] || {}
+ return key ? object[key] : object
+ } else {
+ parents[vueId] = this
+ this.$on('hook:destory', () => {
+ delete parents[vueId]
+ })
+ }
+ }
+
+ Vue.prototype.$setScopedSlotsParams = function (name, value) {
+ const vueId = this.$options.propsData.vueId
+ const object = center[vueId] = center[vueId] || {}
+ object[name] = value
+ if (parents[vueId]) {
+ parents[vueId].$forceUpdate()
+ }
+ }
+
+ Vue.mixin({
+ destroyed () {
+ const propsData = this.$options.propsData
+ const vueId = propsData && propsData.vueId
+ if (vueId) {
+ delete center[vueId]
+ delete parents[vueId]
+ }
+ }
+ })
+}
+
export default function parseBaseApp (vm, {
mocks,
initRefs
}) {
initEventChannel()
+ initScopedSlotsParams()
if (vm.$options.store) {
Vue.prototype.$store = vm.$options.store
}