简单介绍 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 找到被使用过的语句。

相关推荐
众生回避4 分钟前
鸿蒙ms参考
前端·javascript·vue.js
洛千陨5 分钟前
Vue + element-ui实现动态表单项以及动态校验规则
前端·vue.js
GHUIJS1 小时前
【vue3】vue3.5
前端·javascript·vue.js
&白帝&1 小时前
uniapp中使用picker-view选择时间
前端·uni-app
魔术师卡颂1 小时前
如何让“学源码”变得轻松、有意义
前端·面试·源码
谢尔登1 小时前
Babel
前端·react.js·node.js
ling1s1 小时前
C#基础(13)结构体
前端·c#
卸任2 小时前
使用高阶组件封装路由拦截逻辑
前端·react.js
Estrella162 小时前
经典 web 页面排版:三栏布局
前端·css·面试
lxcw2 小时前
npm ERR! code CERT_HAS_EXPIRED npm ERR! errno CERT_HAS_EXPIRED
前端·npm·node.js