提交 298bb93d 编写于 作者: Q qiang

fix: 头条小程序支持解构插槽 question/80187

上级 aaeb31e1
const compiler = require('@dcloudio/uni-mp-weixin/lib/uni.compiler.js') const compiler = require('@dcloudio/uni-mp-weixin/lib/uni.compiler.js')
const path = require('path')
const t = require('@babel/types')
function generateJsCode (properties = '{}') {
return `
wx.createComponent({
generic:true,
props: ${properties},
render: function(){}
})
`
}
function generateCssCode (filename) {
return `
@import "./${filename}"
`
}
function hasOwn (obj, key) {
return Object.prototype.hasOwnProperty.call(obj, key)
}
module.exports = Object.assign({}, compiler, { module.exports = Object.assign({}, compiler, {
directive: 'tt:' directive: 'tt:',
resolveScopedSlots (slotName, {
genCode,
generate,
ownerName,
parentName,
parentNode,
resourcePath,
paramExprNode,
returnExprNodes,
traverseExpr
}, state) {
if (!state.scopedSlots) {
state.scopedSlots = {}
}
const baseName = `${ownerName}-${parentName}-${slotName}`
let componentName = baseName
if (!hasOwn(state.scopedSlots, baseName)) {
state.scopedSlots[baseName] = 0
}
if (state.scopedSlots[baseName]) {
componentName = baseName + state.scopedSlots[baseName]
}
state.scopedSlots[baseName]++
// parentNode.attr['generic:scoped-slots-' + slotName] = componentName
if (!parentNode.attr.generic) {
parentNode.attr.generic = {}
}
parentNode.attr.generic[slotName] = componentName
// 生成 scopedSlots 文件,包括 json,js,wxml,wxss,还需要更新 owner 的 usingComponents
if (!state.files) {
state.files = {}
}
const extname = path.extname(resourcePath)
// TODO 需要存储 resourcePath 相关 json
const templateFile = resourcePath.replace(ownerName + extname, componentName + extname)
const templateContent = generate(traverseExpr(returnExprNodes, state), state)
state.files[templateFile] = templateContent
const jsFile = resourcePath.replace(ownerName + extname, componentName + '.js')
const objectProperties = []
if (t.isObjectPattern(paramExprNode)) {
paramExprNode.properties.forEach(property => {
const key = property.key
const value = property.value
const valueObjectProperties = [
t.objectProperty(t.identifier('type'), t.nullLiteral())
]
if (t.isIdentifier(value)) {
if (value.name !== key.name) {
state.errors.add(`解构插槽 Prop 时,不支持将${key.name}重命名为${value.name},重命名后会影响性能`)
}
} else if (t.isAssignmentPattern(value)) {
valueObjectProperties.push(t.objectProperty(t.identifier('default'), value.right))
}
objectProperties.push(t.objectProperty(key, t.objectExpression(valueObjectProperties)))
})
} else {
state.errors.add(`目前仅支持解构插槽 ${paramExprNode.name},如 v-slot="{ user }"`)
}
const jsContent = generateJsCode(genCode(t.objectExpression(objectProperties), true))
state.files[jsFile] = jsContent
try {
// TODO 使用 getPlatformExts 在单元测试报错,改从 state.options.platform 判断
const { getPlatformExts } = require('@dcloudio/uni-cli-shared')
const styleExtname = getPlatformExts().style
const styleFile = resourcePath.replace(ownerName + extname, componentName + styleExtname)
const styleContent = generateCssCode(ownerName + styleExtname)
state.files[styleFile] = styleContent
} catch (error) { }
// 用于后续修复字节跳动小程序不支持抽象节点导致的问题
const fixExtname = '.fix'
const extFile = resourcePath.replace(ownerName + extname, componentName + fixExtname)
state.files[extFile] = `${resourcePath.replace(ownerName + extname, ownerName)},${parentName},${componentName},scoped-slots-${slotName}`
if (!state.generic) {
state.generic = []
}
// 存储,方便后续生成 json
state.generic.push(componentName)
return ''
}
}) })
...@@ -65,8 +65,13 @@ function processElement (ast, state, isRoot) { ...@@ -65,8 +65,13 @@ function processElement (ast, state, isRoot) {
Object.keys(ast.attr.generic).forEach(scopedSlotName => { Object.keys(ast.attr.generic).forEach(scopedSlotName => {
slots.push(scopedSlotName) slots.push(scopedSlotName)
}) })
if (platformName === 'mp-toutiao') {
// 用于字节跳动小程序模拟抽象节点
ast.attr.generic = `{{${JSON.stringify(ast.attr.generic)}}}`.replace(/"/g, '\'')
} else {
delete ast.attr.generic delete ast.attr.generic
} }
}
if (slots.length && platformName !== 'mp-alipay') { // 标记 slots if (slots.length && platformName !== 'mp-alipay') { // 标记 slots
ast.attr['vue-slots'] = '{{[' + slots.reverse().map(slotName => `'${slotName}'`).join(',') + ']}}' ast.attr['vue-slots'] = '{{[' + slots.reverse().map(slotName => `'${slotName}'`).join(',') + ']}}'
} }
......
...@@ -6,7 +6,8 @@ const { ...@@ -6,7 +6,8 @@ const {
getPlatformExts getPlatformExts
} = require('@dcloudio/uni-cli-shared') } = require('@dcloudio/uni-cli-shared')
const { const {
getComponentSet getComponentSet,
getJsonFile
} = require('@dcloudio/uni-cli-shared/lib/cache') } = require('@dcloudio/uni-cli-shared/lib/cache')
const { const {
...@@ -66,6 +67,7 @@ module.exports = function generateComponent (compilation) { ...@@ -66,6 +67,7 @@ module.exports = function generateComponent (compilation) {
const concatenatedModules = modules.filter(module => module.modules) const concatenatedModules = modules.filter(module => module.modules)
const uniModuleId = modules.find(module => module.resource && normalizePath(module.resource) === uniPath).id const uniModuleId = modules.find(module => module.resource && normalizePath(module.resource) === uniPath).id
const styleImports = {} const styleImports = {}
const fixSlots = {}
Object.keys(assets).forEach(name => { Object.keys(assets).forEach(name => {
if (components.has(name.replace('.js', ''))) { if (components.has(name.replace('.js', ''))) {
...@@ -147,6 +149,57 @@ module.exports = function generateComponent (compilation) { ...@@ -147,6 +149,57 @@ module.exports = function generateComponent (compilation) {
} }
} }
} }
// 处理字节跳动小程序作用域插槽
const fixExtname = '.fix'
if (name.endsWith(fixExtname)) {
const source = assets[name].source()
const [ownerName, parentName, componentName, slotName] = source.split(',')
const json = getJsonFile(ownerName)
if (json) {
const data = JSON.parse(json)
const usingComponents = data.usingComponents || {}
const componentPath = path.relative('/', usingComponents[parentName])
const slots = fixSlots[componentPath] = fixSlots[componentPath] || {}
const slot = slots[slotName] = slots[slotName] || {}
slot[componentName] = '/' + name.replace(fixExtname, '')
delete assets[name]
const jsonFile = assets[`${componentPath}.json`]
if (jsonFile) {
const oldSource = jsonFile.__$oldSource || jsonFile.source()
const sourceObj = JSON.parse(oldSource)
Object.values(slots).forEach(components => {
const usingComponents = sourceObj.usingComponents = sourceObj.usingComponents || {}
Object.assign(usingComponents, components)
})
delete sourceObj.componentGenerics
const source = JSON.stringify(sourceObj, null, 2)
jsonFile.source = function () {
return source
}
jsonFile.__$oldSource = oldSource
}
const templateFile = assets[`${componentPath}${getPlatformExts().template}`]
if (templateFile) {
const oldSource = templateFile.__$oldSource || templateFile.source()
let templateSource
Object.keys(slots).forEach(name => {
const reg = new RegExp(`<${name} (.+?)></${name}>`)
templateSource = oldSource.replace(reg, string => {
const props = string.match(reg)[1]
return Object.keys(slots[name]).map(key => {
return `<block tt:if="{{generic['${name.replace(/^scoped-slots-/, '')}']==='${key}'}}"><${key} ${props}></${key}></block>`
}).join('')
})
})
templateFile.source = function () {
return templateSource
}
templateFile.__$oldSource = oldSource
}
}
}
}) })
} }
if (process.env.UNI_FEATURE_OBSOLETE !== 'false') { if (process.env.UNI_FEATURE_OBSOLETE !== 'false') {
......
...@@ -229,6 +229,11 @@ export function initProperties (props, isBehavior = false, file = '') { ...@@ -229,6 +229,11 @@ export function initProperties (props, isBehavior = false, file = '') {
type: String, type: String,
value: '' value: ''
} }
// 用于字节跳动小程序模拟抽象节点
properties.generic = {
type: Object,
value: null
}
properties.vueSlots = { // 小程序不能直接定义 $slots 的 props,所以通过 vueSlots 转换到 $slots properties.vueSlots = { // 小程序不能直接定义 $slots 的 props,所以通过 vueSlots 转换到 $slots
type: null, type: null,
value: [], value: [],
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册