Tree Shaking | Rollup

683 阅读3分钟

这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战

是什么

概念

Tree shaking 是一个通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code) 行为的术语。依赖于 ES2015中的 import 和 export 语句,用来检测代码模块是否被导出、导入,且被 JavaScript 文件使用。可使最终文件具有简洁的结构和最小化大小。

针对上述的描述,挑出几个关键字:

  1. Dead Code:无用代码,顾名思义,表示不会被执行,不会被使用的代码。

    if (false) {console.log('output')}    // 不被执行
    
  2. 移除未引用代码:dead code elimination,简称 DCE。Tree-Shaking 是代表此行为的术语。

  3. 依赖于 ES6 的 Module 特性:通过import & export两个命令,编译时就可确认模块之间依赖关系,以及输入和输出的变量。利用这一点,可分析判断哪些代码不影响输出,将其作为 Dead Code 移除。

原理

查找资料,发现 Tree Shaking 这概念最早由 rollup 提出,# 官方说明 如下:

In addition to enabling the use of ES modules, Rollup also statically analyzes the code you are importing, and will exclude anything that isn't actually used. (除了支持使用ES模块外,Rollup还静态分析您正在导入的代码,并排除任何实际未使用的代码)

虽已知 Tree Shaking 功能依赖 ES6 的 Module 特性,但思考一下:rollup 具体如何实现?

简易流程如下:

graph LR
源代码 --JS解析器--> 抽象语法树AST --> 遍历分析作用域 --> AST转换 --> 生成新代码

PS:具体详见 # 链接传送门

为什么

Q:浅谈一下编译代码时,为何要进行 Tree Shaking 呢?

A:消除无用代码,使得编译生成的文件体积减少,即浏览器加载的文件大小变小,加载脚本时间相对减少

所以,Tree Shaking 也可理解为是性能优化的一种手段:目前的前端项目,往往在页面中引入一个 JS 入口文件,而 JS 脚本需要先通过网络加载,然后执行,减少其体积,利于缩短整体资源加载的执行时间。

怎么样

又一次,先抛出问题:利于 Tree Shaking 的代码要怎么编写?

移除目标

先来了解下 DCE 的主要移除目标

  1. 无用变量、函数移除

    import {name, age, usedfunc, unusedfunc} from './demo.js'
    
    console.log(name)
    usedfunc();   
    // 编译时应需移除未被执行的age、unusedfunc
    
  2. 无用类移除

    import {usedfunc} from './demo.js'
    import common from './common.js'
    
    usedfunc();   
    // 编译时应需移除未被执行的common
    

    查找资料,得知低版本的 webpack 、rollup 不能消除无用类,如 webpack 3

    为什么呢?答:存在 sideEffects(副作用)

sideEffects

可以理解成:一个函数会、或者可能会对函数外部变量产生影响的行为

如上述代码,输入的 common 虽然未使用但有可能:

  • 使用了原型链
  • 为window添加了属性
  • 立即执行函数引用了外部变量

针对这些情况,就不可轻易将 common 移除,一旦影响运行,执行报错,结果就不是性能优化了!

后续的 webpack 支持 sideEffects 配置,rollup 提供了 treeshake 配置,可指定模块是否存在副作用,更为针对性地删除未使用的代码。

代码编写

为了更好地 Tree Shaking,建议:

  • 减少使用 export default输出,可通过任意个命名输出,后续按需加载

    • Vue 3.x 减少全局 API,提供一些响应式等API,需进行具名引用

      import { defineComponent, reactive, computed, watch, toRefs } from 'vue'
      
  • 不写带有副作用的代码,具体见上 sideEffects

  • 开发库可优先选择 rollup

    • rollup 提供该功能,比简单地运行自动缩小器来检测编译输出代码中未使用的变量更有效

    • Vite 基于 rollup

链接传送门

Last but not least

如有不妥,请多指教~