前端创新实践 - 自研编译器助力小程序架构升级
如果无法正常显示,请先停止浏览器的去广告插件。
1. 前端创新实践
自研编译器助力小程序架构升级
万安文 | vivo互联网用户运营前端专家
2. 目录
01 02 03 04
背景介绍 技术选型 实践与挑战 总结规划
3.
4. 现有 Wepy 小程序需要迁移到 UniApp 架构
尽量降低迁移的人工成本
01
架构迁移
02
迁移后的产出物能更好的复用
原
因
预期效果
Wepy 小程序
03
提升规模化迁移效率
1. 导购等小程序基于Wepy构建,epy进入维护阶段
2. 公司内的前端生态主要围绕 Vue 技术栈体系
04
统一技术栈,实现更好的跨端开发
5.
6. 从迁移周期、方案实现难易,心智成本等综合因素考虑迁移方案的技术选型
最终选择通过编译的方式实现架构升级
01
迁移周期
人工迁移
02
渐进式迁移
03
编译器迁移
很长(25人/天) 中(15人/天) 秒级迁移
方案差异 配置、工程化、API等等 wepy-loader 降低了工程化
差异问题实现成本较高 编译器抹平架构差异,
对团队较高。规模化优势
知识迁移成本 高 中 低
方案复杂度 低 中等偏高 高
(以中型项目为单位)
7.
8. 01
编译原理在前端的应用
02
单文件组件编译转换核心设计
03
实践
编译器思路和实现
整体编译系统流水线设计
04 Parser 解析器架构设计与实现
05 Transform 转换器架构设计与实现
1.Transform-template 模板转换器设计思路
2.Transform-template 模板转换器实现
3.Transfrom-component 组件转换器设计思路
4.Transfrom-component 组件转换器实现
9. 编译原理在前端的应用
现代前端领域在编译方面有大量应用
前端已经进入工程化编译时代:
应用范围广泛
应用框架: Vue、React、JSX
语言:ES6+,Typescript
代码格式化:prettier
代码静态检查:eslint
基础架构完善
大量成熟基础编译工具,Babel、Webpack、Vite 等
社区活跃,生态丰富
es-build、SWC 等工具的兴起
10. 单文件组件分析
wepy和 vue 单组件文件对比
Script模块
相同点
代码结构一致
script/template/style
三部分组成
不同点
API 差异
模板语法完全不同
依赖注入差异
Template模块
Style模块
Wepy 文件结构
Vue 文件结构
11. 单文件组件编译转换设计
通过静态编译/运行期注入将 wepy组件 转为 uniapp组件
Sryles
Transform
(Less/sass/scss)
Sryles
(Less/sass/scss)
AST转换
01
静态编译实现编译期确定性的语法转换
Chameleon
Transpiler
模板,wepy组件,page,app等
Parser Template
解析代码内容 (Pug/wxml)
(expression)
Transform
Template
AST转换
静态编译模块
Script
02
request / wepy async api 等
Script
Wepy特有结构
wpy
运行期polyfill注入,解决项目中对 wepy
的API依赖解耦
Transform
AST转换
原始wpy文件
Chameleon
adaptor
运行期polyfill适配模块
Wepy独有Runtime逻辑的解耦和适配
vue
12. 整体编译系统流水线设计
通过编译流水线设计实现了全流程项目转换自动化
1. 组件通过 parser -> transform -> generate 三个核心步骤生成 UniApp 代码
2. 自动识别不同的项目资源进行不同的流水线转换
Parser解析器 Transform转换器 Generate代码生成器
Source ->
Wepy AST Wepy AST ->
UniApp AST UniApp AST ->
UniApp Vue
Project
analyze
项目资源文件分析
Wepy
项目
UniApp
项目
静态资源文件转换器
Wepy-Code -> UniApp Code
13. Parser 解析器架构设计与实现
通过 Parser 解析器将源码转为 AST
Wepy Code
cheerio
01
02
针对script使用
babel-core 进行语法解析
针对wxml模版借助 cheerio 等工具
高效提取并最终转换为 AST
parser-template
Template Script
cheerio babel/core
Style
Wepy AST
14. Transform 转换器架构设计
通过 Transform 转换器将 Wepy AST 转换为 UniApp AST
Transfom
01
style 模块代码平移
transform-style
02
03
template 模块代码处理模板
语法差异、wxml 适配
script 模块代码处理 API 、
公共方法及依赖注入
Wepy
AST
transform-
template
transform-
script
wxml
template
app wxs
page component
UniApp
AST
15. Transform -Template 模板转换器设计
基于 Wepy 和 Vue 模板语法对比来梳理设计思路:
通过文件比对、源码阅读来分析模板语法异同
Wepy 模板
模
板
差
异
Uni-app 模板
01 wepy 模板语法差异 - xml 命名空间定义指令
02 wxml 引用差异 - 特定的标签用来导入,如 <import />、<include /> 等
wxml vue
wx:if v-if
wx:elif v-else-if
wx:else v-else
wx:for v-for
bindtap @tap
指令的对应转换关系示例
16. Transform -Template 模板转换器实现
模板转换流水线实现:
将传入的 Wepy 模板 AST 进行模板语法及WXML转换。最终生成 UniApp 模板 AST。
1.Wepy AST
2.Template 差异转换
3.WXML 适配
a.处理 wepy 的特殊引入 a.确定引入组件时的传参格式
b.指令、特殊标签等语法的转换 b.确定组件中传入对象的属性范围
c.收集引入的组件信息传递给下游
4. UniApp AST
17. Transfrom-component 组件转换器设计
基于 Wepy 和 Vue 中的 Script 语法对比来梳理设计思路
01
02
03
04
wepy 独有包依赖差异
Props/data/methods
生命周期钩子不同
事件发布/订阅的注册和监听机制不同
Wepy-script
Vue-script
生命周期差异
05
...等等
18. Transfrom-component 组件转换器实现
组件转换流水线实现:
将传入的 Wepy Comp AST 通过预归集和 DSL 语法差异处理,最终输出 UniApp Comp AST。
通过深度遍历,按照预置好的
规则进行数据归集处理
1.Wepy AST
Transform -Template 模板转换器实现
通过归集好的各类 AST 节点数据,
进行各类场景下 DSL 语法的转换
3. DSL 语法差异转换
a.归集 ClassProperty 语法 AST节点 a.Wepy -> Vue 生命周期映射
b.归集非生命周期白名单内的函数 AST 节点 b.生命周期堆叠的处理
c.归集生命周期白名单内的函数 AST 节点 c.函数的处理
d.归集发布/订阅事件注册的函数 AST 节点 d.事件的处理
4. UniApp AST
19. 挑战
典型问题场景回溯
01 Uni-App 中 Template String 语法的适配
02 Scope Style 在 UniApp 中的适配
20. 典型难题回溯-模板字符语法适配
问题一
根因
模板字符串语法(template string)在 UniApp 底层编译不兼容
Wepy 项目的模板语法中充斥着大量的属性表达式,我们需要用模板字符串语法来无缝接入,但是 UniApp 的
style 和 class 属性在编译为小程序时不支持这种写法。
修复方案
wepy 动态表达式
wepy-chameleon compile
vue 动态表达式
uni-app compile
patch-package 自动化补丁
21. 典型难题回溯 - Scope Style 的适配
问题二 Wepy 私有化样式(scope style)的声明在 UniApp 编译后丢失
Wepy 文件转换为 UniApp 项目后正常,UniApp 编译为小程序后就发生了样式覆写丢失。
wepy-chameleon compile
样式异常
uni-app compile
22. 典型难题回溯 - Scope Style 的适配
根因
Vue 和 UniApp 中关于私有化样式的实现机制不同
scopeId 这个用于区分样式作用域的唯一标识在 UniApp 中的处理逻辑不是完全一致的。
vue 的 scope 原理 uniapp 的 scope 原理
主要通过生成data-xx attrbuite 来对页面元素进行定位 主要通过在 class 中生成 data-id 来区分
23. 典型难题回溯 - Scope Style 的适配
解决思路
对 UniApp 的 scope Compile 新增对 template String 场景的处理
我们不需要单独拉分支对 uniApp 提 PR 来处理,同样通过 patch-package 来 fix 即可。
patch package-
添加对isTemplateString的判断
目的:让 uniapp 底层编译 template String 格式的 class/style 属性 转换为数组格式
patch此处
class属性处理支持String template
scopeId 已经可以正确的被 push 到原有的 className 中
24.
25. 后续规划
总结
有力的支撑了业务架构升级,
实现了规模化的代码升级能力
01 覆盖更多的 wepy 项目及场景
02 深挖业务中可以通过编译提效的空间
验证了编译技术对前端
能效大幅度提升
提升了团队对编译的
认知和知识积累
26.