通过 AST 实现组件库替换升级


思否
AST 工程化 React TypeScript Ant Design

转载自 SegmentFault

年初接到一个任务将 semi-ui 替换到 antd,但是能人力预算不太够,所以基于工作量和效率选择了基于 AST 的替换方案。

项目技术栈是 React + tsx

为什么要使用 AST?

AST(Abstract Syntax Tree,抽象语法树)是一种在计算机科学中表示源代码语法结构的树状数据结构。 通过 AST,可以理解代码的结构和含义,实现代码分析、转换和操作。 基于 AST 的替换方案可以快速而准确的对大量代码进行修改,提高开发效率和代码质量。

还有什么方案?

  • 固定字符串替换。对于 import 和 tsx 都有严重的误伤。
  • 正则替换,编写复杂,嵌套难以识别。
  • 手动替换,重复性工作容易失误,耗费工时太多,适合小工作量的情况。

方案比对

方案工作量误伤import组件替换组件属性替换固定字符串替换✅😭😭😒😒正则😒😭😒😒😒手动😭✅✅✅✅AST✅✅✅✅✅

Babel 简介

Babel 是前端领域的必备工具,是一种源码到源码的转译器。 在项目中可以让我们使用一些新的语法(展开运算符) 和 api(@babel/core),会在编译的过程中将 code 转为目标环境所支持的语法并引入 polyfill

Babel 实际应用场景

  • ()=>{} 转为 function(){}
  • Array.isArray 添加 polyfill

除了上面提到的编译转译代码之外,babel 也可以用来做静态分析,分析代码提取信息,然后生成文档 如:

  • 自动国际化处理
  • Linter
  • 压缩混淆,删除死代码,变量名混淆。
  • 模块遍历器,分析 import 移除未被使用的资源模块

Babel 编译原理

babel 是 source to source 的转换,整体编译流程分为三步: • parse:通过 parser 把源码转成抽象语法树(AST) • transform:遍历 AST,调⽤各种 transform 插件对 AST 进⾏增删改 • generate:把转换后的 AST 打印成⽬标代码,并⽣成 sourcemap

Babel 中常⻅节点

  • Literal 是字⾯量的意思,比如说 'are you sure?'
  • Statement 是语句
  • Identifer 是标识符的意思
  • Declaration 声明语句是⼀种特殊的语句,它执⾏的逻辑是在作⽤域内声明⼀个变量、函数、class、import、export 等。
  • Expression 是表达式,特点是执⾏完以后有返回值,这是和语句 (statement) 的区别。
  • Element 是 JSX,分为 JSXElement、JSXOpeningElement、JSXClosingElement 等等

Babel 中常⽤ API

  • @babel/parser :babel parser 叫 babylon,是基于 acorn 实现的,扩展了很多语法,可以⽀ 持 es next(现 在⽀持到 es2020)、jsx、flow、typescript 等语法的解析。babel parser 默认只 能 parse js 代码,jsx、flow、typescript 这些⾮标准的语法的解析需要指定语法插件。
  • @babel/traverse :parse 出的 AST 由 @babel/traverse 来遍历和修改,⽀持指定要遍 历的 AST 节点,指定 visitor 函数。babel 会在遍历 parent 对应的 AST 时调⽤相应的 visitor 函
  • 数。@babel/types :遍历 AST 的过程中需要创建⼀些 AST 和判断 AST 的类型,这时候就需要 @babel/types 包。t.isIfStatement 创建,t.assertIfStatement ⽤于判断。
  • @babel/template :⽀持通过代码来⽣成 ast 进⾏替换。相⽐ parser ,template ⽀持不同粒 度
  • @babel/generator :AST 转换完之后就要打印成⽬标代码字符串,通过 @babel/generator 包
  • @babel/helper-module-imports

替换实战

存在的问题

  • 项⽬技术栈 React + tsx
  • 839 个⽂件中,使⽤ 63 个组件,共计 2485 次使⽤,最多⼀个组件被使⽤了 299 次。
  • 项⽬周⼀、周三四,上线时间不定。更新频率较⾼
  • 没有测试同学,研发⾃测。测试环境⾃测,⽆等待时间,即可上线

实战脚本

替换属性值&替换属性名

  • Space 不⽀持 vertical 需要改为 direction=“vertical”
  • 替换 onChange 事件调⽤
  • 提取 children 构建为 options
  • 替换 Size 改为新的映射关系
  • 将 Link 解构赋值

整体⽅案

  • 统计待升级组件,确认替换范围和影响(839 个⽂件中,使⽤ 63 个组件,共计 2485 次使⽤)
  • 项⽬格式化 npx eslint —fix ./src , prettier —write ’*/.{tsx,ts}’ , 暂 不开启编辑器⾃动格式化
  • 分批替换(⻚⾯维度、组件维度)每周替换4个组件,以低频⾼优作为参考项。
  • 与业务⽅沟通确定上线节奏及变更影响范围,周⼆推 test,周四推 online,提前⼀周在业务群公布。替换计划固定变更影响范围。
  • 测试验收(⾃测&test环境)功能、样式
  • ⽀持快速回滚。依赖 turbo 实现快速回滚,采⽤临时 release 分⽀,实现需求迭代和组件替换不冲突,遇到问题也可以快速回滚。
  • 加⼊业务 feedback 群,遇到问题优先回滚⽌损。
  • 所有替换⼯作完成后,开启⾃动格式化,移除⽆效代码。

优缺点&改进

优点: • ⽀持增量替换,不阻塞业务开发 • ⽀持快速回滚 • 适合⼤批量重复性⼯作 • 适合 jsx 这种嵌套关系的内容修改,可以减少误伤

缺点: • 有⼀定的学习成本(当然可以使⽤ GitHub Copilot 、ChatGpt 减少⼀些⼯作量), • 需要收集组件差异(这⾥可以考虑提取属性和⽅法,然后抓取⽂档中的定义做⽐对) • 编码成本(如果替换数量不多,可以考虑⼿动替换)

总结

最后我们再来回顾⼀下 @babel/parser 、 @babel/traverse 、 @babel/types 、 @babel/template 、 @babel/generator 这些库的作⽤。

也可以想想有什么应⽤场景是可以落地的?⽐如说 • ⾃动国际化,通过脚本提取所有⽂案。 • 增加⼀些个性化的格式化规则,⾃闭合标签,优化 imoprt 导⼊顺序。

传送⻔

  • Babel 插件通关秘籍 - zxg_神说要有光 - 掘⾦⼩册
  • https://astexplorer.net/
  • Stackblitz 测试地址:Express Starter - StackBlitz

原文:SegmentFault

评论

评论功能即将开放。你也可以通过 邮件GitHub Issue 反馈。