简单介绍 farm 中的 tree-shaking

前言

鉴于大家可能对 rust 不是很了解,就尽量不贴代码了,用图和文字来描述整个过程。

由于我的 rust 水平也是一坨大便,如果有讲的不对的地方,谢谢大哥们指正。

正文

前置条件

首先做 tree-shaking 前,我们的模块关系肯定是梳理完毕的,比如 a 模块中引用了模块 b,那么 b 就是 a 的 dependence,基于这个条件,我可以把整个 modules 看出一个图,里面也包括了循环依赖啊,一个模块被多次引用啊这些玩意。

Shake步骤

大致分为以下5个步骤。

1. 排序 sort modules

为什么要 sort modules?

  1. 找到所有模块的引入顺序,保证在该模块做 tree shaking 的时候,以它作为 dependence 的模块都已经 tree shaking 完毕。

就像这个图,比如 B 模块中 import 了 D 模块中的 name,C 模块中 import 了 D 模块中的 age ,那么在 D 模块做 shake 的时候,我们必须保证 B 和 C 已经 shake 过了,不然我们不知道谁使用了 D 模块中的内容。

  1. 找到模块关系中的循环依赖的环,因为循环依赖的模块是不做 shake 处理的。

怎么做

  1. 首先对模块图进行深度优先遍历(先输出子元素,再输出父元素),记录下访问过的模块的 id,重复的模块不再重新访问,如果在访问的时候发现循环依赖,则把这几个模块加入到记录循环模块的数组中。
  1. 将 sort 后的 modules 数组反转
    原因

    图中反转后的顺序就变成了 [a, d, h, i, j, c, g, b, f, e] ;

    观察 f 在上图中,反转前,其实是 b 先引用的 f ,反转后,由于 c 后面没有 f,但是 shake c 的时候,我们就能知道 c 引用了 f 中的内容,所以轮到 f 模块的时候,我们已经能知道 c 和 b 中都引用了哪些内容,就可以进行 shake 了。

  2. 将循环依赖链路中的 module 都标记为 side-effects: true ,后续不进行 shake。

2. 中间操作

  1. 把所有的入口文件(index.tsx)标记为 side-effects,不做 shake
  2. 把非 js|jsx 模块,和外部引入的模块(external)排除,不做 shake。

3. 为每个需要 tree shake 的 module 生成一个 treeModule

根据 module 已有的一些信息,生成一个专门用于 tree shake 的结构体

yaml 复制代码
{
  module_id: 模块id,
  side_effects: 模块是否有副作用,
  module_system: esm 或者 cjs,
  stmt_graph:
  	记录模块de ast 中 语句(statement) 和 变量(ident) 的关系,cjs 为空,因为不做 shake,
  used_exports:
    模块中被使用的导出,side_effects 为 true 的,记为全部导出被使用(不做 shake),不然初始值
    为空数组(后续遍历时记录被使用的 exports 然后添加进来)
}

4. 遍历 treeModule,开始进行 shake

  1. 如果 module 不是 esm ,那么把 module 中所有引入的 module 的 used_exports 也记为 ALL,不做 shake

  2. 如果 module 是 esm,但是 side_effects 为 true,则根据引入模块的语句,按需把引用的内容作为 used_exports 的标记。

    比如:
    import b from 'b'; 就把 b 模块标记为 used_exports::ALL;
    import { b } from 'b'; 就把 b 的 tree shake 结构体中的 used_exports 数组中加入 b。

  3. 如果 module 是 esm 且 side_effects 为 false,就开始正常的 shake 操作。

    1. 先根据前面当前模块 used_exports 中的标记和 ast 树的语句分析,找出被使用过的语句,然后通过对语句中的变量分析,反推出使用了那些引入的依赖,得到 used_import 和 used_export
    2. 然后和上文的步骤一样,根据引入的依赖标记引入模块中的 used_eports。
  4. 把一部引入的模块都标记 side_effects 为 true

5. 删除所有导出都没有被使用的模块

如何删除无用的语句

简单介绍介绍 shake 步骤中第四步中,是如何删除 useless 的语句的。

  1. 首先在生成 treeModule 的时候,把文件中的每个语句都分别进行初始化,生成一个对应的 stmt_id 和对应的语句信息,包括语句的 export、import 信息,语句中使用了哪些变量,定义了哪些变量,是否是自执行(比如 for,while)。

  2. 通过收集到的 usedExports 可以知道哪些 export 的内容被使用了,通过语句中收集的变量,可以知道那些语句是被使用了的,然后依次反推,哪些变量被使用,又可以得知哪些语句被使用了,最后就能得到 used_statement,然后删除没有被使用到的语句。

  3. 最后统计下变量中使用的 import 语句中的内容,标记引入模块中的 usedExport,继续遍历下一个 Module。

盗图:

由于我的画图水平也是一坨大便,就偷一下 pshu 老师的图来帮助理解一下,如果通过 export 找到被使用过的语句。

相关推荐
想起你的日子7 分钟前
Android studio 实现弹出表单编辑界面
java·前端·android studio
LuckyLay1 小时前
Vue百日学习计划Day9-15天详细计划-Gemini版
前端·vue.js·学习
水银嘻嘻7 小时前
12 web 自动化之基于关键字+数据驱动-反射自动化框架搭建
运维·前端·自动化
小嘟嚷ovo8 小时前
h5,原生html,echarts关系网实现
前端·html·echarts
十一吖i8 小时前
Vue3项目使用ElDrawer后select方法不生效
前端
只可远观8 小时前
Flutter目录结构介绍、入口、Widget、Center组件、Text组件、MaterialApp组件、Scaffold组件
前端·flutter
周胡杰8 小时前
组件导航 (HMRouter)+flutter项目搭建-混合开发+分栏效果
前端·flutter·华为·harmonyos·鸿蒙·鸿蒙系统
敲代码的小吉米8 小时前
前端上传el-upload、原生input本地文件pdf格式(纯前端预览本地文件不走后端接口)
前端·javascript·pdf·状态模式
是千千千熠啊8 小时前
vue使用Fabric和pdfjs完成合同签章及批注
前端·vue.js
九月TTS9 小时前
TTS-Web-Vue系列:组件逻辑分离与模块化重构
前端·vue.js·重构