关于前端:精准的打包-Webpack-的-Tree-Shaking

38次阅读

共计 4405 个字符,预计需要花费 12 分钟才能阅读完成。

作者:神 Q 超人
译者:前端小智
起源:medium

有幻想,有干货,微信搜寻 【大迁世界】 关注这个在凌晨还在刷碗的刷碗智。

本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试残缺考点、材料以及我的系列文章。

前阵子在和敌人聊 Webpack 的时候,忽然提到 Tree Shaking,但很羞愧的是我没有方法好好阐明 Webpack 是如何做到 Tree Shaking 的,因而就趁这个年假的第一天抽空读 Webpack 的文件,而后把了解到的心得写下来,如果你也有趣味,就一起看上来吧 🙌。

Tree Shaking 是什麽

Tree Shaking 是个优化的形式,在 JavaScript 中用来示意移除没用的代码的一个常见术语,之所以叫做 Tree Shaking 的由来仿佛是指说“当你鼎力摇摆一棵树的时候,树上就只会留着绿色的叶子,其余枯叶都会落到地上”,而那些绿色的叶子就是打包过后的文件中,真正有用到的代码。

在应用时要留神的是,Tree Shaking 只可能应用在 static structure(例如:importexport 上),像是 dynamic structure require 就没方法被侦测到。举例来说,import 要载入某个 module 应用的话就肯定要在文件的最上方,但 require 能够在任何中央应用,例如以下場景就必須要等到 runtime 才會晓得 module 是什么:

let module = null;

if (Math.random() * 10 > 5) {module = require('module1');
} else {module = require('moudle2');
}

那开始理解 Tree Shaking 的工作前,应该会有些人好奇,就算本人素来就没有特地在 Webpack 中设置 Tree Shaking,然而没有用的代码也都会被移除呀!

那是因为 Tree Shaking 的执行须要 ModuleConcatenationPlugin(图一),而 Webpack 里另外有个 mode,如果你始终没有特地去设置 mode 的值,那 mode 就预设会是 production(图二),而后 production 的预设选项中就会开启 ModuleConcatenationPlugin(也是图二),因而平时不会特地留神到也不奇怪,因为 Webpack 都帮你做好了。

Tree Shaking 的运作

因为 Production 会帮你关上 ModuleConcatenationPlugin,所以待会咱们试验的时候,要把 mode 改成 none(Webpack 文件说 none 为关掉所有优化设置的模式)。

这边会附上简略的 初始化示例配置,有趣味的话,能够把它 clone 下来一起玩看看。

首先在 src 下建设一个 math.jsstring.js,接着个别写下一个办法做 export,别离是 addcomposeString

const add = (a, b) => a + b;

export default {add};

const composeString = (a, b) => `${a} ${b}`;

export default {composeString};

关上 src 下的 index.js,把 addcomposeStringimport,但只应用 add 办法:

import {add} from './math';
import {addString} from './string';

console.log(add(1, 2))

最初到 terminal 中执行 npm run build 或是 webpack 做打包,打包完结后,会发现尽管咱们只有 import add 做应用,然而打包后的档案内容还是会有 composeString

不过这很失常,毕竟咱们还没有做任何解决,Webpack 在打包时也不知道你哪些代码到底有没有用到,就没方法帮你把 composeString 移除。

那么到底什么样的代码是有用的,怎么是没用的呢??

  1. 最显著的定义应该是,如果有被执行就代表有用到。像是下面例子的 add 一样。
  2. side effect 的代码也是被用到的。像是上方的 index.js,看起来什么办法都没有提供,然而执行时却会在 console 中留下 log,除此之外,会扭转执行环境的 polyfill 也是有 side effectlibrary

第一种状况绝对容易分辨,但如果是第二种状况的话,能够抉择用 Webpack 中的 sideEffects 属性来设置。

sideEffects

sideEffects 能够被设置为 Boolean 或是 Array,当你把它设置为 false 的时候,代表该我的项目是不会有 sideEffects 的,也就是一律用 export 判断是否应用。另外 sideEffects 会依赖 providedExports,用来找出我的项目中所有 exportmodule

以下是 sideEffects 的应用形式:

{
  "name": "tree-shaking",
  "sideEffects": false,
  "version": "1.0.0",
  ...
}

只有在 package.json 中加上 sideEffects,并且将值设定为 flase,就代表该我的项目内所有的代码都没有 side effect,因而 Webpack 在打包的时候,就能够把没有用到的 export 代码给移除。

加上 sideEffects 后打包,就不会看到 composeString 在后果裡了:

那当初咱们再到 src 中建设另一个 polyfill.js,在 ployfill.js 里为 Array 建设自定义的办法,再把它 import index.js 中:

index.js

import './polyfill';
import {add} from './math';
import {addString} from './string';

console.log([].customMethod());

polyfill.js

Array.prototype.customMethod = () => {console.log('customMethods');
};

如果咱们去打包上方的代码,polyfill.js 会因为没有任何 export,所以不会被 providedExports 抓到,也就不会被打包到 Production,这会导致我的项目如果有应用到 Array 的 customMethod,在执行时就会出错。面对这种状况,就必须要在 sideEffects 属性中告知,polyfill.js 是有 side effect 的。设置办法如下:

{
  "name": "tree-shaking",
  "sideEffects": ["./src/polyfill.js"],
  "version": "1.0.0",
  ...
}

如此一来,polyfill.js 就会间接被打包了:

最初要留神两件事件:

  1. 如果各位的我的项目中也有 import.css 款式来用的话,也记得要将 .css 结尾的文件名放到 sideEffects,例如 sideEffects: ["*.css"]
  2. webpack.config.js 裡的 optimization 也有 sideEffects,但在这裡设置的值是针对 node_modules 中的。

useExported

useExported 的作用和 sideEffects 都是用来判断是否该移除代码,但依据 Webpack 文件内的阐明,useExported 才是真正的 Tree Shaking:

usedExports 会应用 terser 判断代码有没有 side effect,如果没有用到,又没有 side effect 的话,就会在打包时替它标记上 unused harmony,并在 minify(用 Uglifyjs 或其余工具)的时候移除。

在测试 usedExports 之前,先到 math.js 裡退出 squareexport

const add = (a, b) => a + b;

const square = (a, b) => a * b;

export {add, square};

接下来到 webpack.config.js 中退出 optimization.usedExports

module.exports = {
  ...
  optimization: {usedExports: true,}
};

而后对我的项目进行打包,就会发现仅仅是 export,但没有应用的 square 会被标记上 unused harmony export

接著咱们应用 uglifyjs-webpack-plugin,把没有用到的 square 从树上摇摆下来:

npm install -d uglifyjs-webpack-plugin

webpack.config.js 的设置如下:

const UglifyJsPlugin = require('uglifyjs-webpack-plugin');

module.exports = {
  ...
  optimization: {
    usedExports: true,
    minimize: true,
    minimizer: [
        new UglifyJsPlugin({
            uglifyOptions: {compress: { unused: true},
                mangle: false,
                output: {beautify: true}
            },
        })
    ],
  }
};

设置完 minimizer 后,再打包一次,就能看见 square 曾经被移除了:

usedExportssideEffects 不同的是,usedExports 能够以陈述句为单位去判断是否有 side effect,然而 sideEffects 能够让 Webpack 在打包的时候,间接略过一整个文件,只有是呈现在 sideEffect 裡的文件就是间接打包,也不必透过 terser 评估副作用。

总结

  1. Tree Shaking 只能在 static structure 应用,如果我的项目中的 babel 会将 static structure 编译成 dynamic structure 的话,要另外设置。
  2. 应用 sideEffects 时,要写在 package.json,如果是要对第三方函式库优化,要写在 webpack.config.js 裡的 optimization
  3. usedExports 才是 Tree Shacking,应用时会主动判断没应用的代码,并标记 unused harmony 的注解,要移除的话要另外应用 minify

代码部署后可能存在的 BUG 没法实时晓得,预先为了解决这些 BUG,花了大量的工夫进行 log 调试,这边顺便给大家举荐一个好用的 BUG 监控工具 Fundebug。

原文:
https://medium.com/starbugs/%…

交换

有幻想,有干货,微信搜寻 【大迁世界】 关注这个在凌晨还在刷碗的刷碗智。

本文 GitHub https://github.com/qq44924588… 已收录,有一线大厂面试残缺考点、材料以及我的系列文章。

正文完
 0