释放webpack tree-shaking潜力之webpack-deep-scope-analysis-plugin

2023-05-16

在上周末广州举办的 feday 中, webpack 的核心开发者 Sean 在介绍 webpack 插件系统原理时, 隆重介绍了一个中国学生于 Google 夏令营, 在导师 Tobias 带领下写的一个 webpack 插件, https://github.com/vincentdchan/webpack-deep-scope-analysis-plugin , 这个插件能够大大提高 webpack tree-shaking 的效率.

tree-shaking 目前的缺陷

tree-shaking 作为 rollup 的一个杀手级特性, 能够利用 ES6 的静态引入规范, 减少包的体积, 避免不必要的代码引入, webpack2 也很快引入了这个特性, 但是目前, webpack 只能做比较简单的解决方案, 比如:

这个例子中, webpack 会寻找引入变量的引用, 当发现没有对 isNumber 的引用时, 就会去除 isNumber 的代码. 这其实不太实用, 毕竟在现在的 vscode 中, 没有引用的变量在 ide 中都会灰显提示, 一般不会犯这种 import 某个模块却不用的错误了.

如果是接下来这种引入方式呢, 我写了一个 demo 如下

这个例子非常简单, 如果用图来表示是这样

在 index.js 中引入了 func.js 中的 func2, 并没有引入 func1, 但是 func1 引入了 lodash.webpack 检查的时候发现 func.js 中的确用到了 lodash, 所以不会把 lodash 去掉. 实际上, 我们根本没用到它.

webpack-deep-scope-analysis-plugin 就可以解决这种判断.

插件效果

引入前

引入后

85.8kb -> 不到 1kb

当然, 我这里是标题党了, 因为这里直接把一个 lodash 库给去掉了, 所以变化才这么惊人. 但是即使在实际项目中, 我们也能轻易用一个插件减少大量的不必要的引入.

原理

那么这个插件是怎么去解决这个问题的呢? 这里根据原作者在 Medium 上写的文章, 简单介绍一下他的做法.

webpack 的原理, 其实就是遍历所有的模块, 把它们打包成一个文件, 在这个过程中, 它就知道哪些 export 的模块有被使用到. 那我们同样也可以遍历所有的 scope(作用域), 简化没有用到的 scope, 最后只留下我们需要的.

上图中, func5 层层引用 fun4 fun3 fun2 fun1, 最后解析出来其实只使用了 deepEqual 模块.

什么是 scope 呢, 其实 scope 在各个语言中都有存在, 在 Wikipedia 中是作为计算机术语, 有更详细的解释, 我觉得可以翻译为作用域或者上下文, 在 ECMAScript 中, 有以下明确的定义:


   
  1. // module scope start
  2. // Block
  3. { // <- scope start
  4. } // <- scope end
  5. // Class
  6. class Foo { // <- scope start
  7. } // <- scope end
  8. // If else
  9. if (true) { // <- scope start
  10. } /* <- scope end */ else { // <- scope start
  11. } // <- scope end
  12. // For
  13. for (;;) { // <- scope start
  14. } // <- scope end
  15. // Catch
  16. try {
  17. } catch (e) { // <- scope start
  18. } // <- scope end
  19. // Function
  20. function() { // <- scope start
  21. } // <- scope end
  22. // Scope
  23. switch() { // <- scope start
  24. } // <- scope end
  25. // module scope end

复制代码

在 ES6 中, module 是一种根作用域, 只有 function 和 class 才能作为子作用域被导出, 所以我们解析的时候, 不会把所有的 scope 都作为节点算进去.

我们提到的这个 webpack 插件, 正是内置了这样一个 scope 分析器, 它能够从入口文件中分析出 scope 的引用关系, 最后排除掉所有没有用到的模块.

当然, 这个插件也并不是自己做了所有的事情, 它也是依赖于了前人的工作. https://github.com/estools/escope 是一个分析 ES 中 scope 的工具, 插件作者将它改成了 ts 版本集成到了插件中, 并且利用了 webpack 暴露的接口, 可以解析出来的模块的 AST 树, 基于这个 AST 就可以交给 escope 分析出 scope 的引用关系.

一些边际用例

凡事不能完美, 这个插件也有一些情况会导致判断失误

情况一: 重复赋值变量

比较典型的是以下这个例子:


   
  1. import { isNull } from 'lodash-es';
  2. var fun = 1;
  3. fun = function scope(...args) {
  4. return isNull(...args);
  5. }
  6. export { fun }

复制代码

这个例子中 fun 变量一开始被赋值为数字, 然后被赋值成一个函数, 但是 scope 分析器会直接跳过这个变量, 不把它当作一个单独的 scope.

情况二: 纯函数


   
  1. // copy from rambda/es/allPass.js
  2. import _curry1 from './internal/_curry1';
  3. import curryN from './curryN';
  4. import max from './max';
  5. import pluck from './pluck';
  6. var allPass = /*#__PURE__*/_curry1(function allPass(preds) {
  7. return curryN(reduce(max, 0, pluck('length', preds)), function () {
  8. var idx = 0;
  9. var len = preds.length;
  10. while (idx < len) {
  11. if (!preds[idx].apply(this, arguments)) {
  12. return false;
  13. }
  14. idx += 1;
  15. }
  16. return true;
  17. });
  18. });
  19. export default allPass;

复制代码

在这个例子中, import allPass 会导致_curry1 的运行, 因此它不会被当作一个单独的 scope, 因为它可能会有一些 "副作用", 比如改变某个全部变量, 对全局造成影响. 所以作者给了个方案, 可以在这个函数前加 /*#__PURE__*/, 这样就会把这个函数视为无副作用的纯函数, 如果我们没有 import allPass, 它引用的其他模块都会被去除.

最佳实践

首先, 要用到 tree-shaking, 必然要保证引用的模块都是 ES6 规范的. 这也是为什么我在前面的 demo 中, 引入的是 lodash-es 而不是 lodash.

在项目中, 注意要把 babel 设置 module: false, 避免 babel 将模块转为 CommonJS 规范. 引入的模块包, 也必须是符合 ES6 规范, 并且在最新的 webpack 中加了一条限制, 即在 package.json 中定义 sideEffect: false, 这也是为了避免出现 import xxx 导致模块内部的一些函数执行后影响全局环境, 却被去除掉的情况.

未来

当时跟这位插件作者沟通, 他说将来有可能 Tobias 会把这个插件内置到 webpack 中, 这也是符合 webpack4 零配置的趋势. 但是我们也看得到, 要将前端工程的 dead code elimination 做到和其他静态语言一样好, 靠这些工具是远远不够的, 模块自身也必须配合做到符合规范.

参考链接:

github项目地址:

https://github.com/vincentdchan/webpack-deep-scope-analysis-plugin

Medium原文出处:

https://medium.com/webpack/better-tree-shaking-with-deep-scope-analysis-a0b788c0ce77

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

释放webpack tree-shaking潜力之webpack-deep-scope-analysis-plugin 的相关文章

  • post-css 未从 node_modules 找到路径

    我目前有一个 Angular 项目 我希望使用 purgecss 清除 css 我已经一切正常 但是当我导入 node modules 时 它很困难 因为它找不到位于 node modules 文件夹中的路径 我有当前的app scss f
  • webpack - require('node_modules/leaflet/leaflet.css')

    所以我正在尝试使用构建一个地图应用程序webpack and leaflet 我可以要求leaflet js从我的map js文件 但我无法在不出现错误的情况下调用 leaflet css 我现在的webpack config js好像 u
  • ReactJS React-pdf 错误“无法加载 PDF 文件。”经过一些尝试

    我创建了一个 React js 应用程序create react app我正在尝试react pdf查看 pdf 我遇到的问题是我的代码有时有效 有时无效 当我第一次加载应用程序时 pdf 总是加载得很好 但如果我访问网站上的其他链接 ur
  • webpack - 如何将捆绑包提取到各个组件

    我想从bundle js 一个webpack文件 中解压 提取所有组件和js文件 我只留下这个文件 我已经用谷歌搜索并尝试了几种方法来解压捆绑js文件 但它没有成功 我也尝试过该解决方案 请查找参考 如何提取Webpack中的bundle
  • Vue.js 严格模式下不允许对一个属性进行多个定义

    再会 我们正在使用 Vuejs Vuex vue router 构建我们的应用程序https github com vuejs vue hackernews 2 0 https github com vuejs vue hackernews
  • Webpack 不包括 ProvidePlugins

    我正在开发一个小型试用 Web 应用程序 它使用 vue webpack 模板 https github com vuejs templates webpack https github com vuejs templates webpac
  • Angular js中两个$scope数组的区别

    有没有办法返回 angularjs 范围内存在的两个数组之间的差异 例如 scope user1 a b scope user2 a b c d 这两者的区别应该给我另一个 scope user3 c d Underscore js对此有不
  • Webpack 5 和 ESM

    我想我已经阅读了 SO 上的每个线程以及互联网上的每个相关页面 所有内容都有一些问题的变体 I want 使用 webpack 捆绑我的 Web 应用程序 在我的源 js 中使用 ES 模块并将它们转译为更广泛的浏览器支持 在我的 webp
  • 在 Objective-C 中从异步块增加变量

    我在 Objective C 中开发的服务遇到了一些难题 该服务的目的是解析核心数据实体列表并为每个对象下载相应的图像文件 该服务的原始设计因太多同时下载请求而阻塞了我的网络服务器 为了解决这个问题 我将负责执行下载请求的代码移至递归方法中
  • 如何修复 STL 样式容器以容纳不完整或抽象类型?

    几天前 我尝试以与 STL 容器相同的风格编写一个基本的树实现 现在我尝试在我的代码中使用它 但是有两件事似乎不起作用 但可以说std vector 即 使用不完整类型和使用抽象类型 如何修复我的树实现以获得此功能 我尝试稍微压缩一下我的代
  • CSS 模块:如何禁用文件的本地范围?

    我在一个新的 React 项目中使用 CSS 模块 通过 Webpack css 加载器 尽管它工作得很好 但我在获取 SCSS 时遇到了困难反应选择 https github com JedWatson react select上班 我想
  • Webpack 5 - 资产模块 - 缺少 url-loader 功能 - postTransformPublicPath

    我想按照建议切换到 webpack 5 asset 模块 不幸的是我错过了 webpack url loader 的函数 postTransformPublicPath path any gt any 由于我们应用程序的结构 资产的公共区域
  • 为什么 Webpack 忽略我的 CSS 文件?

    我正在尝试让 webpack 将我的 CSS 文件 使用 PostCSS 编译为单独的文件 从文档来看 这似乎正是 ExtractTextPlugin 应该做的 但是 我无法让 webpack 对我的 CSS 文件执行任何操作 相关项目结构
  • 如何在 Vue.js 中排除文件(例如配置文件)?

    https cli vuejs org config configurewebpack https cli vuejs org config configurewebpack https cli vuejs org config chain
  • 块作用域变量

    这将编译 class X public static void main String args int a 2 int a 3 这不会 class X public static void main String args int a 2
  • 生产中的 Webpack:为什么 React Native 会出现错误?

    我有一个测试应用程序 安装了以下内容 dependencies express 4 14 0 react 15 3 2 react dom 15 3 2 devDependencies babel 6 5 2 babel core 6 18
  • 如何获取调用函数的“this”值?

    如果我有一个这样的函数 function foo this console log this function bar bar prototype func function foo this var test new bar test f
  • 如何在 Material UI 的 webpack 构建中包含 Roboto 字体?

    For a 进步网络应用程序基于材质用户界面 http www material ui com 反应 并构建Webpack 我如何正确包含 Roboto 字体 以便该应用程序不依赖于 Google 服务器并且字体也可以工作offline T
  • 通过 :: 调用包中的函数是一个好习惯吗

    我正在编写一些 R 函数 这些函数在其他包中使用一些有用的函数 例如stringr and base64enc 不打电话好不好library or require 首先加载这些包但要使用 直接引用我需要的功能 比如stringr str m
  • Javascript/jQuery 变量未给出预期值

    和我之前的其他人一样 我也在 Javascript 的范围内苦苦挣扎 那并试图阅读该死的东西 我已经检查了关于这个问题的一些先前的线程 但我似乎无法让它们正确地应用于我的问题 在下面的示例中 我想操纵中的值tagsArr数组 一旦数组已完全

随机推荐

  • 用模式一实现用户登录功能

    login jsp为显示登录表单和处理登录请求的页面 xff0c 登录成功后跳转到logonSuccess jsp页面 xff0c 登录失败时重新显示登录表单和失败的帮助信息 xff0c 以便用户重新登录 xff1b 如果已登录用户再次访问
  • UDP通信 (C语言实现)

    直接看代码吧 v乛 乛 udp server c 文件信息 文 件 名 udp server c 创 建 人 文件创建日期 年 月 日 描 述 UDP 回射服务器程序
  • jni中使用extern "C"的原因

    首先 cplusplus这个宏是微软自定义宏 xff0c 大小是个整数 xff1a cplusplus This macro is defined when the C 43 43 compiler is in use You can us
  • VmWare虚拟机设置ubuntu和windows之间的共享文件夹

    一般在进行编程作业的时候 xff0c 我们会采用 开发在Windows中编辑源代码 xff0c 在linux中编译 执行源代码 这往往需要需要将在Windows下编辑好的源代码上传到linux系统种进行编译 怎么来进行上传呢 xff1f 其
  • C++的最后一道坎|百万年薪的程序员

    导语 C 43 43 的起源可以追溯到 40 年前 xff0c 但它仍然是当今使用最广泛的编程语言之一 xff0c C 43 43 发明人Bjarne Stroustrup 一开始没想到 C 43 43 会获得如此大的成功 xff0c 他说
  • Modbus 协议

    1 主站 xff1a 可以进行读写操作 从站 xff1a 只能被动响应主站操作 2 一个 Modbus 网络只有一个主站 xff0c 可以多个从站 xff08 主站不用连在两端 xff09 485 通讯 1 接线 最多 254 个站 xff
  • 数据结构对齐

    xfeff xfeff 对齐的算法 xff1a 由于各个平台和编译器的不同 xff0c 现以本人使用的gcc version 3 2 2编译器 xff08 32位x86平台 xff09 为例子 xff0c 来讨论编译器对struct数据结构
  • 关于示波器测485串口波特率的使用方法总结

    之前没有用过示波器 xff0c 更不知道怎么来测试串口的波特率 xff0c 结果遇到一客户说我们产品的波特率达不到9600 xff0c 只有9100 xff0c 为了验证这一说法 xff0c 我们只能自己测试一下 说明 xff1a 产品通过
  • C语言对寄存器封装

    一 封装外设 用C语言代码把外设地址映射用宏定义封装 span class hljs comment 外设基地址 span span class hljs preprocessor define PERIPH BASE unsigned i
  • JavaJDK下载安装与环境配置(Windows 10 超详细的图文版教程 )

    前言 xff1a 对于很多初学者来说 xff0c 我想可能很多人都会遇到JDK环境变量的配置问题 明明就是按照度娘上的教程去一步步配置的 xff0c 但还是会有很多的人出现配置不成功的问题 所以今天在这里分享一下windows 10 系统下
  • win32 获取窗口句柄的方法

    第一种方法是根据窗口句柄值获取窗口句柄 使用spy 43 43 获取指定窗口的窗口句柄值 xff0c 因为句柄值是16进制数 xff0c 所以前面加0x 然后进行强制转换为HWND类型 HWND hWnd 61 HWND 0x0028072
  • 当设置display:inline;时li的宽度无效的解决方法

    若制作导航栏时 xff0c 使用列表li 的定义时 xff0c 若想加上一个背景图 xff0c 这时候若定义li的一个属性为 li display inline width 83px height 30px xff0c 则浏览器会无视后面的
  • js文本框或者按钮鼠标悬停提示说明文字

    html页面中很多元素会用到文本提示 xff0c 当鼠标悬停之后显示一段说明文字 显示说明性文字 function tips id str t 61 getTop document getElementById id document ge
  • localstorage兼容ie8以下浏览器的问题

    最近在做一个网站 xff0c 由于希望尽可能减小服务器的压力 xff0c 也想提高网站的运转速度 xff0c 就想尽可能少的在服务器上读写数据以及下载重复数据 xff0c 需要重复使用的数据 xff0c 就储存在本地 xff0c 能在本地进
  • HTTP请求返回状态码中301与302的状态码区别

    一 xff0e 官方说法 301 xff0c 302 都是HTTP状态的编码 xff0c 都代表着某个URL发生了转移 xff0c 不同之处在于 xff1a 301 redirect 301 代表永久性转移 Permanently Move
  • java防止 csrf 攻击 --- 采用 spring .

    CSRF xff08 Cross site request forgery xff09 xff0c 中文名称 xff1a 跨站请求伪造 xff0c 也被称为 xff1a one click attack session riding xff
  • Gson解析数组多类型元素

    why used gson Gson is a Java library that can be used to convert Java Objects into their JSON representation It can also
  • js中获取时间new Date()详细介绍

    1 var myDate 61 new Date Date 返回当日的日期和时间 getDate 从 Date 对象返回一个月中的某一天 1 31 getDay 从 Date 对象返回一周中的某一天 0 6 getMonth 从 Date
  • 如何让一个行内元素(如一张图片)在div中居中

    xff08 1 xff09 第一种 xff1a 用vertical align lt div class 61 34 method1 34 gt lt span class 61 34 tiptop 34 gt lt span gt lt
  • 释放webpack tree-shaking潜力之webpack-deep-scope-analysis-plugin

    在上周末广州举办的 feday 中 webpack 的核心开发者 Sean 在介绍 webpack 插件系统原理时 隆重介绍了一个中国学生于 Google 夏令营 在导师 Tobias 带领下写的一个 webpack 插件 https gi