首页 > 其他分享 >webpack原理(2):ES6 module在Webpack中如何Tree-shaking构建

webpack原理(2):ES6 module在Webpack中如何Tree-shaking构建

时间:2023-03-18 17:56:50浏览次数:44  
标签:ES6 tree Tree webpack Webpack 模块 import shaking

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 来说,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

标签:ES6,tree,Tree,webpack,Webpack,模块,import,shaking
From: https://www.cnblogs.com/zhoulujun/p/17231334.html

相关文章

  • webpack原理(3):Tapable源码分析及钩子函数作用分析
    webpack本质上是一种事件流的机制,它的工作流程就是将各个插件串联起来,而实现这一切的核心就是Tapable,webpack中最核心的负责编译的Compiler和负责创建bundles的Compilation......
  • webpack原理(1):Webpack热更新实现原理代码分析
    热更新,主要就是把前端工程文件变更,即时编译,然后通知到浏览器端,刷新代码。服务单与客户端通信方式有:ajax轮询,EventSource、websockt。客户端刷新一般分为两种:整体页......
  • TreeMap
    TreeMap是有序map,通过key进行排序1.TreeMap是如何实现去重和排序的?TreeMap实现了SortedMap接口,它是一个key有序的Map类。TreeMap的默认排序规则:根据key元素的compareTo......
  • 【CF1491H Yuezheng Ling and Dynamic Tree】(分块)
    原题链接题意给定一棵大小为\(n\)的\(1\)为根节点的树,树用如下方式给出:输入\(a_2,a_3,\dots,a_n\),保证\(1\leqa_i<i\),将\(a_i\)与\(i\)连边形成一棵树。接下......
  • Tree结构UI优化显示
    整体UI面板绘制参照https://www.cnblogs.com/babashi9527/p/17146645.html接着UI面板的设计;实现树形结构菜单的方式有很多种,每一种优化UI显示的方式可能存在较大差异;我......
  • ES6 解构基础用法和应用!
    ES6解构基础用法和应用!注意:解构只与语法有关,不与let,var,const相关 一、数组解构取值//数组---取值letarr=["张三","李四","王五"];leta=arr[0];letb=......
  • ES6.面向对象(主)和面向过程
    面向过程优点:效率高,因为不需要实例化对象。缺点:耦合度高,扩展性差,不易维护(例如:每个步骤都要有,不然就不行)面向对象优点:耦合低(易复用),扩展性强,易维护,由于面向对象有封装、......
  • 538. Convert BST to Greater Tree
    GivenaBinarySearchTree(BST),convertittoaGreaterTreesuchthateverykeyoftheoriginalBSTischangedtotheoriginalkeyplussumofallkeysgrea......
  • 如何优雅在webpack项目实现mock服务器
    为什么需要mock至于平时开发为什么需要mock数据,应该大多数的同学都非常清楚了;如果前后端同步开发的话,少不了这一步,在需求评审,技术评估等流程通过后,前后端就会约定接口a......
  • tkinter中treeview隔行显示不同的颜色
    隔行显示不同颜色的代码,这个牵涉到背景颜色,在3.8版的tkinter,要加多一些代码,才能让背景颜色起作用。这段要多加的代码就是:deffixed_map(option):return[elmforel......