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

相关推荐
Ocean☾1 分钟前
前端基础-html-注册界面
前端·算法·html
Dragon Wu3 分钟前
前端 Canvas 绘画 总结
前端
CodeToGym8 分钟前
Webpack性能优化指南:从构建到部署的全方位策略
前端·webpack·性能优化
~甲壳虫9 分钟前
说说webpack中常见的Loader?解决了什么问题?
前端·webpack·node.js
~甲壳虫13 分钟前
说说webpack proxy工作原理?为什么能解决跨域
前端·webpack·node.js
Cwhat14 分钟前
前端性能优化2
前端
熊的猫1 小时前
JS 中的类型 & 类型判断 & 类型转换
前端·javascript·vue.js·chrome·react.js·前端框架·node.js
瑶琴AI前端1 小时前
uniapp组件实现省市区三级联动选择
java·前端·uni-app
会发光的猪。2 小时前
如何在vscode中安装git详细新手教程
前端·ide·git·vscode
我要洋人死3 小时前
导航栏及下拉菜单的实现
前端·css·css3