Tree-shaking 最早由打包工具 Rollup 提出

DCE 作用于模块内(webpack 的 DCE 通过 UglifyJS 完成),而 Tree-shaking 则是在打包的时候通过模块之间的信息打包必须的代码。

Webpack 从 2 开始也支持 Tree-shaking,对于一个模块,没有被使用过的引入代码并不会被打包

DCE

  • AST 对 JS 代码进行语法分析后得出的语法树 (Abstract Syntax Tree)。AST语法树可以把一段 JS 代码的每一个语句都转化为树中的一个节点。

  • DCE Dead Code Elimination [ɪˌlɪmɪˈneɪʃn],在保持代码运行结果不变的前提下,去除无用的代码。这样的好处是:

    • 减少程序体积

    • 减少程序执行时间

    • 便于将来对程序架构进行优化

  • 而所谓 Dead Code 主要包括:

    • 程序中没有执行的代码 (如不可能进入的分支,return 之后的语句等)

    • 导致 dead variable 的代码(写入变量之后不再读取的代码)

tree shaking 是 rollup 作者首先提出的。相比于排除不使用的代码,tree shaking 其实是找出使用的代码。

tree shaking

可以先回顾下《再唠叨JS模块化加载之CommonJS、AMD、CMD、ES6 》

  • CommonJS 的设计过于灵活,对静态分析不友好。

  • ES6 module 则有诸多限制:比如说只能在文件的顶部 import(CommonJS 的 require 语法允许在文件的任意位置调用),export { ... } 语法保证了导出的变量不会是 getter/setter 之类奇怪的东西(这个 block 不是一个 Object),变量也不能被重新绑定。以上种种设计可以让分析器一定程度上判断出导入和导出变量的关系,让这个插件的实现成为了可能。

基于ES6的静态引用,tree shaking 通过扫描所有 ES6 的export,找出被import 的内容并添加到最终代码中。 webpack 的实现是把所有import 标记为有使用/无使用两种,在后续压缩时进行区别处理。

根本原理则是作用域分析。在编译器领域,还有超级多各种高大上的静态分析方法(比如说数据流分析),但是对于 ES 来说,他们实现的难度太大。据我所知,现在还没有针对 JS 的,能在生产环境能用的,基于数据流分析的优化器。这也是为啥现在这些打包器还不能去除没有用到的类成员方法(class method)webpack tree shaking 只处理顶层内容,例如类和对象内部都不会再被分别处理

所谓作用域分析,就是可以分析出代码里面变量所属的作用域以及他们之间的引用关系。有了这些信息,就可以推导出导出变量和导入变量之间的引用关系

而对于 webpack 来说,webpack 可以通过 entry 和 module 之间的调用得知对于一个 module 来说,哪个变量是会被使用到的。就如同上文的例子 :我的插件可以从 webpack 得知 file1.js 的导出变量 one 被使用了。我的插件通过分析出模块中的作用域,遍历引用到的作用域,找到真正需要 import 的变量,比如说 isNumber,然后再把结果返回 webpack。

  • 使用 ES6 Module:不仅是项目本身,引入的库最好也是 es 版本,比如用 lodash-es 代替 lodash。另外注意 TypeScript 和 Babel 的配置是否会把代码编译成非 es module 版本。

  • 最纯函数调用使用 PURE 注释:由于无法判断副作用,所以对于导出的函数调用最好使用 PURE 注释,不过一般来说有相关的 babel 插件自动添加。

合理模块设计才是减少代码体积的关键

启用tree shaking

首先源码必须遵循 ES6 的模块规范 (import&export),如果是 CommonJS 规范 (require) 则无法使用。

在编写支持 tree-shaking 的代码时,导入方式非常重要。你应该避免将整个库导入到单个 JavaScript 对象中。当你这样做时,你是在告诉 Webpack 你需要整个库, Webpack 就不会摇它

以流行的库 Lodash 为例。一次导入整个库是一个很大的错误,但是导入单个的模块要好得多。当然,Lodash 还需要其他的步骤来做 tree-shaking,但这是个很好的起点。

// 全部导入 (不支持 tree-shaking)
import _ from 'lodash';
// 具名导入(支持 tree-shaking)
import { debounce } from 'lodash';
// 直接导入具体的模块 (支持 tree-shaking)
import debounce from 'lodash/lib/debounce';

webpack 3 和 4 默认支持,webpack2需要特别配置

webpack2

根据 Webpack 官网的提示,webpack2 支持 tree-shaking,需要修改配置文件,指定 babel 处理 js 文件时不要将 ES6 模块转成 CommonJS 模块,具体做法就是:

在 .babelrc 设置 babel-preset-es2015 的 modules 为 fasle,表示不对 ES6 模块进行处理。

// .babelrc
{
    "presets": [
        ["es2015", {"modules": false}]
    ]
}

webpack 负责对代码进行标记,把import&export标记为 3 类:

  1. 所有import标记为/* harmony import */

  2. 被使用过的export标记为/* harmony export ([type]) */,其中[type]和 webpack 内部有关,可能是binding, immutable等等。

  3. 没被使用过的export标记为/* unused harmony export [FuncName] */,其中 [FuncName]为export的方法名称

之后在 Uglifyjs (或者其他类似的工具) 步骤进行代码精简,把没用的都删除。

webpack tree shaking副作用

pure_funcs

webpack.config.js 增加参数pure_funcs,告诉webpack 那些函数是没有副作用的,你可以放心删除:

plugins: [
  new UglifyJSPlugin({
    uglifyOptions: {
      compress: {
          pure_funcs: ['Math.floor']
      }
    }
  })
],

Math.floor这类全局方法不会重命名,才会生效。因此适用性不算太强。

package.json 的 sideEffects

webpack 4 在 package.json 新增了一个配置项叫做sideEffects, 值为false表示整个包都没有副作用;或者是一个数组列出有副作用的模块。详细的例子可以查看 webpack 官方提供的例子

{
  "name": "your-project",
  "sideEffects": false}

这种方式是通过 package.json 的 "sideEffects" 属性来实现的。

concatenateModule 压缩输出

webpack 4  `mode = 'production'

使用 -p(production) 这个 webpack 编译标记,来启用 uglifyjs 压缩插件。

把本来“每个模块包裹在一个闭包里”的情况,优化成“所有模块都包裹在同一个闭包里”的情况。本身对于代码缩小体积有很大的提升,这里也能侧面解决副作用的问题。

参考文章:

Webpack 4 Tree Shaking 终极优化指南 https://juejin.im/post/6844903998634328072

Tree Shaking in Webpack https://juejin.im/post/5c58df43e51d457ffc1bd065

浅谈 ES 模块和 Webpack Tree-shaking https://zhuanlan.zhihu.com/p/43844419

转载本站文章《webpack原理(2):ES6 module在Webpack中如何Tree-shaking构建》,
请注明出处:https://www.zhoulujun.cn/html/tools/Bundler/webpackTheory/8504.html

webpack原理(2):ES6 module在Webpack中如何Tree-shaking构建的更多相关文章

  1. Webpack 中的 Tree Shaking

    Tree Shaking Tree shaking 用于描述移除JavaScript上下文中的未引用代码(dead-code). 为了更方便地理解tree shaking,我们可以将应用程序想象成一棵 ...

  2. 深入 CommonJs 与 ES6 Module

    目前主流的模块规范 UMD CommonJs es6 module umd 模块(通用模块) (function (global, factory) { typeof exports === 'obj ...

  3. es6 module + webpack

    其实在之前本人就看了 es6 里面的一部分内容,当然是阮一峰大神的 ECMAScript 6 入门. 最近闲来无事又来看下,其中 Module 的语法 这章时候,用里面代码跑的时候,理所当然的报错 S ...

  4. webpack原理与实战

    webpack是一个js打包工具,不一个完整的前端构建工具.它的流行得益于模块化和单页应用的流行.webpack提供扩展机制,在庞大的社区支持下各种场景基本它都可找到解决方案.本文的目的是教会你用we ...

  5. webpack原理探究 && 打包优化

    在做vue项目和react项目时,都用到了webpack.webpack帮助我们很好地提高了工作效率,但是一直以来没有对其原理进行探究,略有遗憾. 因为使用一个工具,能够深入了解其原理才能更好地使用. ...

  6. webpack原理类型问题

    1.webpack底层原理 (实现一个webpack) 步骤:1.拿到入口文件的代码并读出来转化为js对象(抽象语法术parser)2.拿到所有模块的依赖 ‘./message.js’,放进数组中 引 ...

  7. Webpack 原理浅析

    作者: 凹凸曼 - 风魔小次郎 背景 Webpack 迭代到4.x版本后,其源码已经十分庞大,对各种开发场景进行了高度抽象,阅读成本也愈发昂贵.但是为了了解其内部的工作原理,让我们尝试从一个最简单的 ...

  8. webpack+react+redux+es6开发模式

    一.预备知识 node, npm, react, redux, es6, webpack 二.学习资源 ECMAScript 6入门 React和Redux的连接react-redux Redux 入 ...

  9. [webpack] 配置react+es6开发环境

    写在前面 每次开新项目都要重新安装需要的包,简单记录一下. 以下仅包含最简单的功能: 编译react 编译es6 打包src中入口文件index.js至dist webpack配置react+es6开 ...

  10. webpack使用tree shaking的问题。及关于UglifyJs不支持ES6的解决方案。

    webpack: plugins:[ new webpack.optimize.UglifyJsPlugin({ compress:{warning:true} }) ] 是的,一些dead code ...

随机推荐

  1. 使用ClosedXml查询Excel文件数据,匹配时间并显示

    使用Nuget包管理器安装ClosedXml包,VS没网在https://www.nuget.org/ 下载后,包源本地安装至项目 函数: private void SelectGrab(Cancel ...

  2. 鬼谷子的钱袋(lgP2320)

    主要思路:二进制拆分. 先将 \(m\) 进行二进制拆分. 注意金币总数有限,也就是说拆分后可能会多出来一组.多出来的这组如果不是 \(2^n\) 就不需要考虑了,因为不会和前面的重复. 接下来考虑重 ...

  3. 一起来探索CSS中margin属性的奥秘吧!!

    作者:WangMin 格言:努力做好自己喜欢的每一件事 众所周知 margin属性 是用来声明当前所设置或者指定元素所有外边距的宽度,或者设置各边上外边距的宽度.一直以来我认为它是一个很简单的属性,但 ...

  4. 文心一言 VS 讯飞星火 VS chatgpt (129)-- 算法导论11.1 4题

    四.用go语言,我们希望在一个非常大的数组上,通过利用直接寻址的方式来实现一个字典.开始时该数组中可能包含一些无用信息,但要对整个数组进行初始化是不太实际的,因为该数组的规模太大.请给出在大数组上实现 ...

  5. 从一道题来看看golang中的slice作为参数时的现象

    1.题目 最近看群友在群里问一道关于golang中slice的题,题目如下: package main import "fmt" func main() { k := []int{ ...

  6. 反转字符串里的单词(leetcode 4.10每日打卡)

    给定一个字符串,逐个翻转字符串中的每个单词.   示例 1: 输入: "the sky is blue"输出: "blue is sky the" 示例 2: ...

  7. MySQL运行在docker容器中会损失多少性能

    前言 自从使用docker以来,就经常听说MySQL数据库最好别运行在容器中,性能会损失很多.一些之前没使用过容器的同事,对数据库运行在容器中也是忌讳莫深,甚至只要数据库跑在容器中出现性能问题时,首先 ...

  8. 一个基于ASP.NET Core完全开源的CMS 解决方案

    本文简介 MixCoreCMS是一个基于.NET Core框架的开源内容管理系统(CMS),提供了丰富的的基础功能和插件,是一款面向未来的企业 Web CMS,可轻松构建任何类型的应用程序.集成了Go ...

  9. 3款国产办公软件,不仅好用,还支持linux国产操作系统

    当提到国产办公软件并支持Linux国产操作系统时,以下是三款备受好评的软件: 1. WPS Office(金山办公套件) WPS Office是中国知名的办公软件套件,也是一款跨平台的应用程序.它包含 ...

  10. DC静态时序分析之时钟篇

    DC静态时序分析之时钟篇博主微信:flm13724054952,不懂的有疑惑的也可以加微信咨询,欢迎大家前来投稿,谢谢! 引言介绍在芯片设计或者FPGA设计里面,根据有无时钟,将电路设计分为时序逻辑电 ...