Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mpx template #1575

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/core/src/platform/patch/web/getDefaultOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,12 @@ export function getDefaultOptions ({ type, rawOptions = {} }) {
}
}
const rootMixins = [{
data: {
_data_v_id: '' // template 透传scoped样式用
},
beforeCreate () {
const _scopeId = this.$vnode.componentOptions.Ctor?.options?._scopeId
this._data_v_id = _scopeId
initProxy(this, rawOptions)
},
created () {
Expand Down
46 changes: 46 additions & 0 deletions packages/webpack-plugin/lib/dependencies/WriteVfsDependency.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const NullDependency = require('webpack/lib/dependencies/NullDependency')
const makeSerializable = require('webpack/lib/util/makeSerializable')

class WriteVfsDependency extends NullDependency {
constructor (filename, content) {
super()
this.filename = filename
this.content = content
}

get type () {
return 'mpx app entry'
}

mpxAction (module, compilation, callback) {
const mpx = compilation.__mpx__
const vfs = mpx.__vfs
if (vfs) {
vfs.writeModule(this.filename, this.content)
}
return callback()
}

serialize (context) {
const { write } = context
write(this.filename)
write(this.content)
super.serialize(context)
}

deserialize (context) {
const { read } = context
this.filename = read()
this.content = read()
super.deserialize(context)
}
}

WriteVfsDependency.Template = class WriteVfsDependencyTemplate {
apply () {
}
}

makeSerializable(WriteVfsDependency, '@mpxjs/webpack-plugin/lib/dependencies/WriteVfsDependency')

module.exports = WriteVfsDependency
4 changes: 4 additions & 0 deletions packages/webpack-plugin/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const FlagPluginDependency = require('./dependencies/FlagPluginDependency')
const RemoveEntryDependency = require('./dependencies/RemoveEntryDependency')
const RecordLoaderContentDependency = require('./dependencies/RecordLoaderContentDependency')
const RecordRuntimeInfoDependency = require('./dependencies/RecordRuntimeInfoDependency')
const WriteVfsDependency = require('./dependencies/WriteVfsDependency')
const SplitChunksPlugin = require('webpack/lib/optimize/SplitChunksPlugin')
const fixRelative = require('./utils/fix-relative')
const parseRequest = require('./utils/parse-request')
Expand Down Expand Up @@ -623,6 +624,9 @@ class MpxWebpackPlugin {
compilation.dependencyFactories.set(RecordRuntimeInfoDependency, new NullFactory())
compilation.dependencyTemplates.set(RecordRuntimeInfoDependency, new RecordRuntimeInfoDependency.Template())

compilation.dependencyFactories.set(WriteVfsDependency, new NullFactory())
compilation.dependencyTemplates.set(WriteVfsDependency, new WriteVfsDependency.Template())

compilation.dependencyTemplates.set(ImportDependency, new ImportDependencyTemplate())
})

Expand Down
65 changes: 61 additions & 4 deletions packages/webpack-plugin/lib/template-compiler/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const setBaseWxml = require('../runtime-render/base-wxml')
const { parseExp } = require('./parse-exps')
const shallowStringify = require('../utils/shallow-stringify')
const { isReact } = require('../utils/env')
const getTemplateContent = require('../utils/get-template-content')

const no = function () {
return false
Expand Down Expand Up @@ -671,13 +672,15 @@ function parse (template, options) {
root = currentParent = getVirtualHostRoot(options, meta)
stack.push(root)
}
options.template = template // processTemplate时需要对template(只用于处理含name的情况)做截取

parseHTML(template, {
warn: warn$1,
expectHTML: options.expectHTML,
isUnaryTag: options.isUnaryTag,
canBeLeftOpenTag: options.canBeLeftOpenTag,
shouldKeepComment: true,
wxmlName: options.wxmlName,
start: function start (tag, attrs, unary) {
// check namespace.
// inherit parent ns if there is one
Expand Down Expand Up @@ -2097,7 +2100,7 @@ function processWebExternalClassesHack (el, options) {
if (staticClass) {
const classNames = staticClass.split(/\s+/)
const replacements = []
options.externalClasses.forEach((className) => {
options.externalClasses?.forEach((className) => {
const index = classNames.indexOf(className)
if (index > -1) {
replacements.push(`$attrs[${stringify(className)}]`)
Expand Down Expand Up @@ -2350,8 +2353,33 @@ function processTemplate (el) {
}
}

function postProcessTemplate (el) {
function processImport (el, options, meta) {
if (el.tag === 'import' && el.attrsMap.src) {
if (!meta.templateSrcList) {
meta.templateSrcList = []
}
if (!meta.templateSrcList.includes(el.attrsMap.src)) {
meta.templateSrcList.push(el.attrsMap.src)
}
}
}

function postProcessTemplate (el, meta, options) {
if (el.isTemplate) {
if (mode === 'web') {
if (!meta.inlineTemplateMap) {
meta.inlineTemplateMap = {}
}
const name = el.attrsMap.name
if (name) {
const content = getTemplateContent(options.template, name)
const filePath = options.filePath.replace(/.mpx$/, `-${name}.wxml`)
meta.inlineTemplateMap[name] = {
filePath,
content
}
}
}
processingTemplate = false
return true
}
Expand Down Expand Up @@ -2486,6 +2514,7 @@ function processMpxTagName (el) {
}

function processElement (el, root, options, meta) {
const initialTag = el.tag // 预存,在这个阶段增加_fakeTemplate值会影响web小程序元素trans web元素
processAtMode(el)
// 如果已经标记了这个元素要被清除,直接return跳过后续处理步骤
if (el._atModeStatus === 'mismatch') {
Expand All @@ -2508,10 +2537,17 @@ function processElement (el, root, options, meta) {
const transAli = mode === 'ali' && srcMode === 'wx'

if (mode === 'web') {
if (initialTag === 'block') {
el._fakeTemplate = true // 该值是在template2vue中处理block转换的template的情况
}
// 收集内建组件
processBuiltInComponents(el, meta)
// 预处理代码维度条件编译
processIfWeb(el)
// 预处理template逻辑
processTemplate(el)
// 预处理import逻辑
processImport(el, options, meta)
processWebExternalClassesHack(el, options)
processComponentGenericsWeb(el, options, meta)
return
Expand Down Expand Up @@ -2569,6 +2605,8 @@ function closeElement (el, meta, options) {

if (mode === 'web') {
postProcessWxs(el, meta)
// 处理web下template逻辑
postProcessTemplate(el, meta, options)
// 处理代码维度条件编译移除死分支
postProcessIf(el)
return
Expand All @@ -2579,7 +2617,7 @@ function closeElement (el, meta, options) {
postProcessIfReact(el)
return
}
const pass = isNative || postProcessTemplate(el) || processingTemplate
const pass = isNative || postProcessTemplate(el, meta) || processingTemplate
postProcessWxs(el, meta)
if (!pass) {
if (isComponentNode(el, options) && !hasVirtualHost && mode === 'ali') {
Expand Down Expand Up @@ -2693,9 +2731,28 @@ function serialize (root) {
result += node.text
}
}
if (node.tag === 'wxs' && mode === 'web') {
if ((node.tag === 'wxs' || node.tag === 'import') && mode === 'web') {
return result
}
if (mode === 'web') {
if (node.tag === 'template' && node.attrsMap && node.attrsMap.name) {
return result
}
if (node.tag === 'template' && node.attrsMap && node.attrsMap.is) {
node.tag = 'component'
node.attrsList.forEach((item) => {
if (item.name === 'is') {
item.name = ':is'
item.value = `'${item.value}'`
}
if (item.name === ':data') {
item.name = 'v-bind'
const bindValue = item.value.replace(/\(|\)/g, '')
item.value = bindValue ? `{${bindValue}, _data_v_id}` : '{ _data_v_id }' // 用于处理父组件scoped情况下template中的样式传递
}
})
}
}
if (node.type === 1) {
if (node.tag !== 'temp-node') {
result += '<' + node.tag
Expand Down
47 changes: 47 additions & 0 deletions packages/webpack-plugin/lib/utils/get-template-content.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
对template.wxml文件做截取
@source原始小程序文件
@name 要匹配的该name的template
*/
module.exports = function (source, name) {
// 使用正则表达式匹配具有 name 的 template 标签及其所有子元素
// 正则表达式使用非贪婪匹配来递归匹配嵌套的 template
const regex = new RegExp(`(<template[^>]*\\bname=["|']${name}["|'][^>]*>).*?`, 'g')

let startIndex = 0
let endIndex = 0
const match = regex.exec(source)
// 逐个处理匹配到的 template 标签及其内容
if (match) {
const matchRes = match[0]
const reg = /<\/?template\s*[^>]*>/g
let n = 0
startIndex = match.index
endIndex = startIndex + matchRes.length
let html = source.substr(endIndex)
while (html) {
const matchRes = html.match(reg)
if (matchRes.length) {
const matchTemp = matchRes[0]
const matchIndex = html.indexOf(matchTemp)
const matchLength = matchTemp.length
const cutLength = matchIndex + matchLength
if (matchTemp.startsWith('</template>')) {
if (n === 0) {
endIndex += cutLength
break
} else {
n--
}
} else {
n++
}
endIndex += cutLength
html = html.substr(cutLength)
}
}
} else {
return ''
}
return source.substring(startIndex, endIndex)
}
2 changes: 2 additions & 0 deletions packages/webpack-plugin/lib/web/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ module.exports = function ({
builtInComponentsMap: templateRes.builtInComponentsMap,
genericsInfo: templateRes.genericsInfo,
wxsModuleMap: templateRes.wxsModuleMap,
templateSrcList: templateRes.templateSrcList,
inlineTemplateMap: templateRes.inlineTemplateMap,
localComponentsMap: jsonRes.localComponentsMap
}, callback)
}
Expand Down
33 changes: 27 additions & 6 deletions packages/webpack-plugin/lib/web/processScript.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ const loaderUtils = require('loader-utils')
const normalize = require('../utils/normalize')
const shallowStringify = require('../utils/shallow-stringify')
const optionProcessorPath = normalize.lib('runtime/optionProcessor')
const wxmlTemplateLoader = normalize.lib('web/wxml-template-loader')
const WriteVfsDependency = require('../dependencies/WriteVfsDependency')
const {
buildComponentsMap,
getRequireScript,
Expand All @@ -22,18 +24,20 @@ module.exports = function (script, {
builtInComponentsMap,
genericsInfo,
wxsModuleMap,
templateSrcList,
inlineTemplateMap,
localComponentsMap
}, callback) {
const { projectRoot, appInfo, webConfig } = loaderContext.getMpx()
const { projectRoot, appInfo, webConfig, __vfs: vfs } = loaderContext.getMpx()

let output = '/* script */\n'

let scriptSrcMode = srcMode
if (script) {
scriptSrcMode = script.mode || scriptSrcMode
} else {
script = { tag: 'script' }
}

output += genComponentTag(script, {
attrs (script) {
const attrs = Object.assign({}, script.attrs)
Expand All @@ -58,17 +62,35 @@ module.exports = function (script, {
content += ` wxsModules.${module} = ${expression}\n`
})
}
content += 'const templateModules = {}\n'
if (templateSrcList?.length) { // import标签处理
templateSrcList?.forEach((item) => {
content += `
const tempLoaderResult = require(${stringifyRequest(this, `!!${wxmlTemplateLoader}!${item}`)})\n
Object.assign(templateModules, tempLoaderResult)\n`
})
}

// 获取组件集合
const componentsMap = buildComponentsMap({ localComponentsMap, builtInComponentsMap, loaderContext, jsonConfig })

// 获取pageConfig
const pageConfig = {}
if (ctorType === 'page') {
Object.assign(pageConfig, jsonConfig)
delete pageConfig.usingComponents
}

if (inlineTemplateMap) { // 处理行内template(只有属性为name的情况)
const inlineTemplateMapLists = Object.keys(inlineTemplateMap)
if (inlineTemplateMapLists.length) {
inlineTemplateMapLists.forEach((name) => {
const { filePath, content } = inlineTemplateMap[name]
loaderContext._module.addPresentationalDependency(new WriteVfsDependency(filePath, content)) // 处理缓存报错的情况
vfs.writeModule(filePath, content) // 截取template写入文件
const expression = `getComponent(require(${stringifyRequest(loaderContext, `${filePath}?is=${name}&isTemplate`)}))`
componentsMap[name] = expression
})
}
}
content += buildGlobalParams({ moduleId, scriptSrcMode, loaderContext, isProduction, webConfig, hasApp })
content += getRequireScript({ ctorType, script, loaderContext })
content += `
Expand All @@ -78,7 +100,7 @@ module.exports = function (script, {
outputPath: ${JSON.stringify(outputPath)},
pageConfig: ${JSON.stringify(pageConfig)},
// @ts-ignore
componentsMap: ${shallowStringify(componentsMap)},
componentsMap: Object.assign(${shallowStringify(componentsMap)}, templateModules),
componentGenerics: ${JSON.stringify(componentGenerics)},
genericsInfo: ${JSON.stringify(genericsInfo)},
wxsMixin: getWxsMixin(wxsModules),
Expand All @@ -87,7 +109,6 @@ module.exports = function (script, {
return content
}
})

callback(null, {
output
})
Expand Down
11 changes: 9 additions & 2 deletions packages/webpack-plugin/lib/web/processTemplate.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ module.exports = function (template, {
const { resourcePath } = parseRequest(loaderContext.resource)
const builtInComponentsMap = {}

let wxsModuleMap, genericsInfo
let wxsModuleMap, genericsInfo, inlineTemplateMap, templateSrcList
let output = '/* template */\n'

if (ctorType === 'app') {
Expand Down Expand Up @@ -103,6 +103,12 @@ module.exports = function (template, {
wxsContentMap[`${resourcePath}~${module}`] = meta.wxsContentMap[module]
}
}
if (meta.inlineTemplateMap) {
inlineTemplateMap = meta.inlineTemplateMap
}
if (meta.templateSrcList?.length) {
templateSrcList = meta.templateSrcList
}
if (meta.builtInComponentsMap) {
Object.keys(meta.builtInComponentsMap).forEach((name) => {
builtInComponentsMap[name] = {
Expand All @@ -118,10 +124,11 @@ module.exports = function (template, {
})
output += '\n'
}

callback(null, {
output,
builtInComponentsMap,
inlineTemplateMap,
templateSrcList,
genericsInfo,
wxsModuleMap
})
Expand Down
Loading
Loading