Skip to content

Commit

Permalink
fix transform ali template
Browse files Browse the repository at this point in the history
  • Loading branch information
hiyuki committed Nov 29, 2023
1 parent 70e7831 commit 2207cc1
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 88 deletions.
16 changes: 8 additions & 8 deletions docs-vuepress/articles/2.9-release-alter.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,15 @@ const escapeMap = {
}
```

与此同时,用户也可以通过传递 `@mpxjs/unocss-plugin`[`escapeMap`](https://mpxjs.cn/api/compile.html#escapeMap) 配置项来覆盖内建的转义规则。
与此同时,用户也可以通过传递 `@mpxjs/unocss-plugin`[escapeMap](https://mpxjs.cn/api/compile.html#escapeMap) 配置项来覆盖内建的转义规则。

#### 原子类分包输出

在 web 中,原子类会被全部打包输出单个样式文件,一般会放置在顶层样式表中以供全局访问,但在小程序中这种全量的输出策略并不是最优的,主要原因在于小程序中可供全局访问的主包体积存在 **2M 大小限制**,主包体积十分紧缺珍贵,Mpx 在构建输出时遵循着分包优先的原则,尽可能充分利用分包体积从而减少对主包体积的占用,再进行原子类产物输出时,我们也遵循了相同的原则。

在Mpx中,我们在收集原子类时同时记录了每个原子类的引用分包,在收集结束后根据每个原子类的分包引用数量决定该原子类应该输出到主包还是分包当中,我们在 `@mpxjs/unocss-plugin` 中提供了 [`minCount`](https://mpxjs.cn/api/compile.html#minCount) 配置项来决定分包的输出规则,该配置项的默认值为2,即当一个原子类被2个或以上分包引用时,会被作为公共原子类抽取到主包中,否则输出到所属分包中,这也是全局最优的策略。当我们想要让原子类输出产物更少地占用主包体积时,我们也可以将`minCount`值调大,让原子类抽取到主包的条件更加苛刻,不过这样也会伴随着原子类分包冗余的增加。
在Mpx中,我们在收集原子类时同时记录了每个原子类的引用分包,在收集结束后根据每个原子类的分包引用数量决定该原子类应该输出到主包还是分包当中,我们在 `@mpxjs/unocss-plugin` 中提供了 [minCount](https://mpxjs.cn/api/compile.html#minCount) 配置项来决定分包的输出规则,该配置项的默认值为2,即当一个原子类被2个或以上分包引用时,会被作为公共原子类抽取到主包中,否则输出到所属分包中,这也是全局最优的策略。当我们想要让原子类输出产物更少地占用主包体积时,我们也可以将`minCount`值调大,让原子类抽取到主包的条件更加苛刻,不过这样也会伴随着原子类分包冗余的增加。

`unocss.config.js` 配置中定义的 `safelist` 原子类默认会输出到主包,为了组件局部使用的 `safelist` 有输出到分包的机会,我们在模版中提供了[`注释配置`](https://mpxjs.cn/api/compile.html#commentConfig)(comments config),灵感来源于 `webpack` 中的魔法注释(magic comments),用户可以在组件模版中通过`注释配置`声明当前组件所需的 `safelist`,对应的原子类也会根据上述的规则输出到主包或分包中,使用示例如下:
`unocss.config.js` 配置中定义的 `safelist` 原子类默认会输出到主包,为了组件局部使用的 `safelist` 有输出到分包的机会,我们在模版中提供了[注释配置](https://mpxjs.cn/api/compile.html#commentConfig)(comments config),灵感来源于 `webpack` 中的魔法注释(magic comments),用户可以在组件模版中通过`注释配置`声明当前组件所需的 `safelist`,对应的原子类也会根据上述的规则输出到主包或分包中,使用示例如下:

```html
<template>
Expand All @@ -107,9 +107,9 @@ const escapeMap = {

在小程序中,自定义组件的样式默认是隔离的,web 中通过全局样式访问原子类的方式不再生效,不过由于小程序提供了[样式隔离配置](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/wxml-wxss.html#%E7%BB%84%E4%BB%B6%E6%A0%B7%E5%BC%8F%E9%9A%94%E7%A6%BB),我们可以将该组件样式隔离配置调整为 `apply-shared` 来获取页面或 app 中定义的原子类,但是当我们在使用传统类名和原子类混合开发或者迁移原子类的过程中,我们往往希望保留原本自定义组件的样式隔离。

针对这种情况,我们在 `@mpxjs/unocss-plugin` 中提供了 [`styleIsolation`](https://mpxjs.cn/api/compile.html#styleIsolation) 配置项,可选设置为 `isolated`|`apply-shared`,当设置为 `isolated` 时每个组件都会通过 `@import` 独立引用主包或者分包的原子类样式文件,因此不会受到样式隔离的影响;当设置为 `apply-shared` 时,只有 app 和分包页面会引用对应的原子类样式文件,自定义组件需要通过配置样式隔离为 `apply-shared` 使原子类生效。
针对这种情况,我们在 `@mpxjs/unocss-plugin` 中提供了 [styleIsolation](https://mpxjs.cn/api/compile.html#styleIsolation) 配置项,可选设置为 `isolated`|`apply-shared`,当设置为 `isolated` 时每个组件都会通过 `@import` 独立引用主包或者分包的原子类样式文件,因此不会受到样式隔离的影响;当设置为 `apply-shared` 时,只有 app 和分包页面会引用对应的原子类样式文件,自定义组件需要通过配置样式隔离为 `apply-shared` 使原子类生效。

在组件分包异步的情况下对应组件即使将样式隔离配置为 `apply-shared` 的情况下,`@mpxjs/unocss-plugin` 也需要将 `styleIsolation` 设置为 `isolated` 才能正常工作,原因在于组件分包异步的情况下,组件被其他分包的页面所引用渲染,由于上述原子类样式分包输出的规则,其他分包的页面中可能并不包含当前组件所需的原子类,只有在 `isolated` 模式下由组件自身引用所需的原子类样式才能保证正常工作,类似于 `safelist`,我们也提供了[`注释配置`](https://mpxjs.cn/api/compile.html#commentConfig)的方式对组件的 `styleIsolation` 模式进行局部配置,示例如下:
在组件分包异步的情况下对应组件即使将样式隔离配置为 `apply-shared` 的情况下,`@mpxjs/unocss-plugin` 也需要将 `styleIsolation` 设置为 `isolated` 才能正常工作,原因在于组件分包异步的情况下,组件被其他分包的页面所引用渲染,由于上述原子类样式分包输出的规则,其他分包的页面中可能并不包含当前组件所需的原子类,只有在 `isolated` 模式下由组件自身引用所需的原子类样式才能保证正常工作,类似于 `safelist`,我们也提供了[注释配置](https://mpxjs.cn/api/compile.html#commentConfig)的方式对组件的 `styleIsolation` 模式进行局部配置,示例如下:
```html
<template>
<!-- mpx_config_styleIsolation: 'isolated' -->
Expand All @@ -128,7 +128,7 @@ Mpx 中使用原子类的详细参考文档如下:

## 输出 web 支持 SSR

近些年来,SSR/SSG 由于其良好的首屏展现速度和SEO友好性逐渐成为主流的技术规范,各类SSR框架层出不穷,未来进一步提升性能表现,在 SSR 的基础上还演进出 [`islands architecture`](https://docs.astro.build/en/concepts/islands/)[`0 hydration`](https://qwik.builder.io/docs/concepts/resumable/) 等更加精细复杂的理念和架构。
近些年来,SSR/SSG 由于其良好的首屏展现速度和SEO友好性逐渐成为主流的技术规范,各类SSR框架层出不穷,未来进一步提升性能表现,在 SSR 的基础上还演进出 [islands architecture](https://docs.astro.build/en/concepts/islands/)[0 hydration](https://qwik.builder.io/docs/concepts/resumable/) 等更加精细复杂的理念和架构。

近两年随着团队对于前端性能的重视,SSR/SSG 技术也在团队业务中逐步推广落地,并在首屏性能方面取得了显著的收效。但由于过去 Mpx 对 SSR 的支持不完善,使用 Mpx 开发的跨端页面一直无法享受到 SSR 带来的性能提升,在 Mpx2.9 版本中,我们对 web 输出流程进行了大量适配改造,解决了 SSR 中常见的内存泄漏、跨请求状态污染和数据预请求等问题,完整实现了基于 Vue 和 Pinia 的 SSR 技术方案。

Expand Down Expand Up @@ -377,7 +377,7 @@ self.webpackChunkmpx_test_2_8 = require('../bundle.js')

我们对模版代码的生成逻辑和生成产物进行分析,发现 webpack 生产模式的默认配置中,很多配置项并不是体积最优的选项,一个典型的例子在于模块/chunk id:为了保障生成产物的内容稳定,来尽可能提升浏览器的缓存利用率,webpack 默认的模块/chunk id 采用 `deterministic` 模式进行生成,该模式下模块 id 为模块源路径的定长数字 hash,比项目模块总数长 1 位。由于在小程序中代码包按照版本的维度进行全量管理,保证文件局部的内容稳定在小程序环境下无正向意义,这就有了优化空间,我们可以简单将模块/chunk id 的生成逻辑改为数字自增,在主小程序中就能节省出上百 KB 总包体积。

类似的可优化点还存在于 chunk 链接代码和模块包装函数当中,我们在 `@mpxjs/[email protected]` 版本中提供了一个新的配置项 [`optimizeSize`](https://mpxjs.cn/api/compile.html#optimizesize),其中整合了一系列模版代码体积优化配置,开启后就能自动优化构建产物中的模版代码体积,在主小程序中,我们开启 `optimizeSize` 后可以减少总包体积约 **540KB**,效果非常显著,下面是上述示例在开启 `optimizeSize` 后的产物对比:
类似的可优化点还存在于 chunk 链接代码和模块包装函数当中,我们在 `@mpxjs/[email protected]` 版本中提供了一个新的配置项 [optimizeSize](https://mpxjs.cn/api/compile.html#optimizesize),其中整合了一系列模版代码体积优化配置,开启后就能自动优化构建产物中的模版代码体积,在主小程序中,我们开启 `optimizeSize` 后可以减少总包体积约 **540KB**,效果非常显著,下面是上述示例在开启 `optimizeSize` 后的产物对比:

```js
var g = {}
Expand Down Expand Up @@ -460,7 +460,7 @@ function render(){

除此之外,我们也大幅优化了 render 函数中的数据收集代码,有效地降低了 render 函数的体积占用。

该优化目前没有默认开启,可以通过 `@mpxjs/webpack-plugin` 中的 [`optimizeRenderRules`](https://mpxjs.cn/api/compile.html#optimizerenderrules) 配置项配置生效范围进行开启,
该优化目前没有默认开启,可以通过 `@mpxjs/webpack-plugin` 中的 [optimizeRenderRules](https://mpxjs.cn/api/compile.html#optimizerenderrules) 配置项配置生效范围进行开启,
全量开启后在主小程序中实测可节省总包体积约 **1507KB**

## 未来规划
Expand Down
31 changes: 21 additions & 10 deletions packages/webpack-plugin/lib/platform/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
const runRules = require('./run-rules')

module.exports = function getRulesRunner ({ type, mode, srcMode, data, meta, testKey, mainKey, waterfall, warn, error }) {
const specMap = {
template: {
wx: require('./template/wx')({ warn, error })
},
json: {
wx: require('./json/wx')({ warn, error })
}
const specMap = {
template: {
wx: require('./template/wx')
},
json: {
wx: require('./json/wx')
}
const spec = specMap[type] && specMap[type][srcMode]
}

module.exports = function getRulesRunner ({
type,
mode,
srcMode,
data,
meta,
testKey,
mainKey,
waterfall,
warn,
error
}) {
const spec = specMap[type] && specMap[type][srcMode] && specMap[type][srcMode]({ warn, error })
if (spec && spec.supportedModes.indexOf(mode) > -1) {
const normalizeTest = spec.normalizeTest
const mainRules = mainKey ? spec[mainKey] : spec
Expand Down
8 changes: 3 additions & 5 deletions packages/webpack-plugin/lib/platform/json/wx/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,8 @@ module.exports = function getSpec ({ warn, error }) {

/**
* 将小程序代码中使用的与原生 HTML tag 或 内建组件 同名的组件进行转化,以解决与原生tag命名冲突问题。
* @param {string} type usingComponents
* @returns input
*/
function webHTMLTagProcesser (type) {
function fixComponentName (type) {
return function (input) {
const usingComponents = input[type]
if (usingComponents) {
Expand Down Expand Up @@ -160,7 +158,7 @@ module.exports = function getSpec ({ warn, error }) {
},
{
test: 'usingComponents',
web: webHTMLTagProcesser('usingComponents')
web: fixComponentName('usingComponents')
},
{
test: 'usingComponents',
Expand Down Expand Up @@ -365,7 +363,7 @@ module.exports = function getSpec ({ warn, error }) {
},
{
test: 'usingComponents',
web: webHTMLTagProcesser('usingComponents')
web: fixComponentName('usingComponents')
},
{
test: 'usingComponents',
Expand Down
3 changes: 1 addition & 2 deletions packages/webpack-plugin/lib/platform/run-rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ module.exports = function runRules (rules = [], input, options = {}) {
if (result !== undefined) {
input = result
}
// rule 内外 waterfall 均为 false 时跳过
if (!rule.waterfall && !waterfall) break
if (!(rule.waterfall || waterfall)) break
}
}
return input
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,58 +8,57 @@ const runRules = require('../run-rules')
module.exports = function normalizeComponentRules (cfgs, spec) {
return cfgs.map((cfg) => {
const result = {}
if (cfg.test) {
result.test = cfg.test
}
// 透传 waterfall 信息
if (cfg.test) result.test = cfg.test
if (cfg.waterfall) result.waterfall = cfg.waterfall
const supportedModes = cfg.supportedModes || spec.supportedModes
// 合并component-config中组件的event 与index中公共的event规则
const eventRules = (cfg.event || []).concat(spec.event.rules)
supportedModes.forEach((mode) => {
result[mode] = function (el, data) {
data = Object.assign({}, data, { el, eventRules })
const testKey = 'name'
let rAttrsList = []
const options = {
mode,
testKey,
data
}
el.attrsList.forEach((attr) => {
const meta = {}
let rAttr = runRules(spec.directive, attr, {
...options,
meta
})
// 指令未匹配到时说明为props,因为目前所有的指令都需要转换
if (!meta.processed) {
rAttr = runRules(spec.preProps, rAttr, options)
rAttr = runRules(cfg.props, rAttr, options)
result[mode] = cfg.skipNormalize
? cfg[mode]
: function (el, data) {
data = Object.assign({}, data, { el, eventRules })
const testKey = 'name'
let rAttrsList = []
const options = {
mode,
testKey,
data
}
el.attrsList.forEach((attr) => {
const meta = {}
let rAttr = runRules(spec.directive, attr, {
...options,
meta
})
// 指令未匹配到时说明为props,因为目前所有的指令都需要转换
if (!meta.processed) {
rAttr = runRules(spec.preProps, rAttr, options)
rAttr = runRules(cfg.props, rAttr, options)
if (Array.isArray(rAttr)) {
rAttr = rAttr.map((attr) => {
return runRules(spec.postProps, attr, options)
})
} else if (rAttr !== false) {
rAttr = runRules(spec.postProps, rAttr, options)
}
}
// 生成目标attrsList
if (Array.isArray(rAttr)) {
rAttr = rAttr.map((attr) => {
return runRules(spec.postProps, attr, options)
})
rAttrsList = rAttrsList.concat(rAttr)
} else if (rAttr !== false) {
rAttr = runRules(spec.postProps, rAttr, options)
rAttrsList.push(rAttr)
}
})
el.attrsList = rAttrsList
el.attrsMap = require('../../template-compiler/compiler').makeAttrsMap(rAttrsList)
// 前置处理attrs,便于携带信息用于tag的处理
const rTag = cfg[mode] && cfg[mode].call(this, el.tag, data)
if (rTag) {
el.tag = rTag
}
// 生成目标attrsList
if (Array.isArray(rAttr)) {
rAttrsList = rAttrsList.concat(rAttr)
} else if (rAttr !== false) {
rAttrsList.push(rAttr)
}
})
el.attrsList = rAttrsList
el.attrsMap = require('../../template-compiler/compiler').makeAttrsMap(rAttrsList)
// 前置处理attrs,便于携带信息用于tag的处理
const rTag = cfg[mode] && cfg[mode].call(this, el.tag, data)
if (rTag) {
el.tag = rTag
return el
}
return el
}
})
return result
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const { isOriginTag, isBuildInTag } = require('../../../../utils/dom-tag-config')

module.exports = function () {
return {
waterfall: true,
skipNormalize: true,
supportedModes: ['web'],
test: (input) => isOriginTag(input) || isBuildInTag(input),
// 输出web时对组件名进行转义避免与原生tag和内建tag冲突
web (el, data) {
const newTag = `mpx-com-${el.tag}`
const usingComponents = data.usingComponents || []
// 当前组件名与原生tag或内建tag同名,对组件名进行转义
// json转义见:platform/json/wx/index.js fixComponentName
if (usingComponents.includes(newTag)) {
el.tag = newTag
}
return el
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ const switchComponent = require('./switch')
const template = require('./template')
const text = require('./text')
const textarea = require('./textarea')
const Nonsupport = require('./unsupported')
const unsupported = require('./unsupported')
const video = require('./video')
const view = require('./view')
const webView = require('./web-view')
const wxs = require('./wxs')
const component = require('./component')
const fixHTMLTag = require('./fix-html-tag')
const fixComponentName = require('./fix-component-name')

module.exports = function getComponentConfigs ({ warn, error }) {
/**
Expand Down Expand Up @@ -80,8 +80,8 @@ module.exports = function getComponentConfigs ({ warn, error }) {

// 转换规则只需以微信为基准配置微信和支付宝的差异部分,比如微信和支付宝都支持但是写法不一致,或者微信支持而支付宝不支持的部分(抛出错误或警告)
return [
fixHTMLTag(),
...Nonsupport({ print }),
...unsupported({ print }),
fixComponentName({ print }),
ad({ print }),
view({ print }),
scrollView({ print }),
Expand Down

0 comments on commit 2207cc1

Please sign in to comment.