IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

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

    linong发表于 2024-10-09 16:22:58
    love 0

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

    项目技术栈是 React + tsx

    为什么要使用 AST?

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

    还有什么方案?

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

    方案比对

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

    Babel 简介

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

    Babel 实际应用场景

    1. 将 ()=>{} 转为 function(){}
    2. 为 Array.isArray 添加 polyfill

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

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

    Babel 编译原理

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

    Babel 中常⻅节点

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

    image.png

    Babel 中常⽤ API

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

    替换实战

    存在的问题

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

    实战脚本

    替换属性值&替换属性名

    1. Space 不⽀持 vertical 需要改为 direction="vertical"
    2. 替换 onChange 事件调⽤
    3. 提取 children 构建为 options
    4. 替换 Size 改为新的映射关系
    5. 将 Link 解构赋值

    整体⽅案

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

    优缺点&改进

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

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

    总结

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

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

    传送⻔

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


沪ICP备19023445号-2号
友情链接