webpack高级应用篇(九):tree shaking(usedExports) 与 sideEffects

tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块语法的 静态结构 特性,例如 importexport

webpack 4 正式版本扩展了此检测能力,通过 package.json"sideEffects" 属性作为标记,向 compiler 提供提示,表明项目中的哪些文件是 “pure(纯正 ES2015 模块)”,由此可以安全地删除文件中未使用的部分。

webpack 基础配置

npm i -D webpack webpack-cli html-webpack-plugin webpack-dev-server

Tip:以下演示为 webpack 5

tree shaking(usedExports)

tree shaking 前

先在开发环境下看一下

webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    
    
  mode: 'development',
  devtool: 'inline-source-map',

  plugins: [
    new HtmlWebpackPlugin(),
  ],
};

src/math.js

export const add = (x, y) => x + y;

export const subtract = (x, y) => x - y;

src/index.js

import {
    
     add } from './math';

console.log(add(1, 2));

从上面看到,我们引用并使用了math.add 函数,没有使用 math.subtract 函数

执行 npx webpack 可以看到,打包结果中 math 模块的两个函数都被打包了
在这里插入图片描述

tree shaking 后

webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    
    
  mode: 'development',
  devtool: 'inline-source-map',

  plugins: [
    new HtmlWebpackPlugin(),
  ],

  optimization: {
    
    
    // 使用 ES module 方式引用的模块将被 tree shaking 优化
    usedExports: true,
  },
};

执行 npx webpack 可以看到,只有 已经使用的 add 函数被暴露出去

在这里插入图片描述
在生产环境看以下效果

webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    
    
  mode: 'production',

  plugins: [
    new HtmlWebpackPlugin(),
  ],
};

执行 npx webpack 可以看到,只有已经使用的 add 函数的执行结果, subtract 函数就是所谓的“未引用代码(dead code)”,也就是说,应该删除掉未被引用的 export。并且代码已经被 webpack 优化精简了
在这里插入图片描述
可以得出结论,tree shaking 会将通过使用 ES module 方式引用的模块中未使用的代码删除掉

tree shaking 两个关键词:1. 使用 ES module 模块方案; 2. 未使用的代码

继续验证

src/index.js

import {
    
     add, subtract } from './math';

console.log(add(1, 2));

从上面看到,我们引用了 add, subtract 但只使用了math.add 函数,没有使用 math.subtract 函数

执行 npx webpack 可以看到,打包结果中依旧只有 add 函数被打包了,未使用过的 subtract 函数被删除了
在这里插入图片描述

sideEffects(副作用)

注意 Webpack 不能百分百安全地进行 tree-shaking。有些模块导入,只要被引入,就会对应用程序产生重要的影响。一个很好的例子就是 全局样式 文件,或者 全局JS 文件。

src/style.css

body {
    
    
    background-color: chocolate;
}

src/todo.global.js

console.log('TODO');

src/index.js

import _ from 'lodash';
import {
    
     add, subtract } from './math';
import './todo.global';
import './style.css';

console.log(add(1, 2));

webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    
    
  mode: 'production',

  plugins: [
    new HtmlWebpackPlugin(),
  ],

  module: {
    
    
    rules: [
      {
    
    
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader',
        ],
      },
    ],
  },

  optimization: {
    
    
    usedExports: true,
  },
};

执行 npx webpack serve ,你会发现 style.csstodo.global.js 都生效了,这是因为这两个文件不是使用的 ESmodules 方式将模块导出(export)的。

Webpack 认为这样的文件有“副作用”。具有副作用的文件不应该做 tree-shaking,因为这将破坏整个应用程序。

Webpack 的设计者清楚地认识到不知道哪些文件有副作用的情况下打包代码的风险,因此webpack 4默认地将所有代码视为有副作用。

这可以保护你免于删除必要的文件,但这意味着 Webpack 的默认行为实际上是不进行 tree-shaking。值得注意的是 webpack 5 默认会进行 tree-shaking。如何告诉 Webpack 你的代码无副作用,可以通过 package.json 有一个特殊的属性sideEffects,就是为此而存在的。

如何告诉 Webpack 你的代码无副作用,可以通过 package.json 有一个特殊的属性 sideEffects,就是为此而存在的。

**sideEffects**有三个可能的值:

  • true:(默认值)这意味着所有的文件都有副作用,也就是没有一个文件可以 tree-shaking

  • false:告诉 Webpack 没有文件有副作用,所有文件都可以 tree-shaking

  • 数组:是文件路径数组。它告诉 webpack,除了数组中包含的文件外,你的任何文件都没有副作用。因此,除了指定的文件之外,其他文件都可以安全地进行 tree-shaking

Tip

“side effect(副作用)” 的定义是,在导入时会执行特殊行为的代码,而不是仅仅暴露一个 export 或多个 export。举例说明,例如 polyfill,它影响全局作用域,并且通常不提供 export。

package.json

{
    
    
  "sideEffects": true, // 所有的文件都有副作用,也就是没有一个文件可以 `tree-shaking`。
}

执行 npx webpack serve 可以发现并无变化,因为这是默认的

package.json

{
    
    
  "sideEffects": false, // 告诉 Webpack 没有文件有副作用,所有文件都可以 `tree-shaking`。
}

执行 npx webpack serve 可以发现上面示例中的 style.csstodo.global.js 都被 tree-shaking

显然 sideEffects 设置为 true 或则 false 显得有些鲁莽极端,你可以使用数组的方式配置文件

package.json

{
    
    
  "sideEffects": ['*.css', '*.global.js'], // 告诉 Webpack 扩展名是 .css 或者 .global.js 文件视为有副作用,不要 `tree-shaking` 
}

执行 npx webpack serve 可以发现上面示例中的 style.csstodo.global.js 都被 tree-shaking

webpack 4 曾经不进行对 CommonJs 导出和 require() 调用时的导出使用分析。

webpack 5 增加了对一些 CommonJs 构造的支持,允许消除未使用的 CommonJs 导出,并从 require() 调用中跟踪引用的导出名称。


解释 tree shaking 和 sideEffects

sideEffectsusedExports(更多被认为是 tree shaking)是两种不同的优化方式。

sideEffects 更为有效 是因为它允许跳过整个模块/文件和整个文件子树。

usedExports 依赖于 terser 去检测语句中的副作用。它是一个 JavaScript 任务而且没有像 sideEffects 一样简单直接。而且它不能跳转子树/依赖由于细则中说副作用需要被评估。


结论

我们学到为了利用 tree shaking 的优势, 你必须…

  • 使用 ES2015 模块语法(即 importexport)。
  • 确保没有编译器将您的 ES2015 模块语法转换为 CommonJS 的(顺带一提,这是现在常用的 @babel/preset-env 的默认行为,详细信息请参阅文档)。
  • 在项目的 package.json 文件中,添加 "sideEffects" 属性。
  • 使用 mode"production" 的配置项以启用更多优化项,包括压缩代码与 tree shaking。

你可以将应用程序想象成一棵树。绿色表示实际用到的 source code(源码) 和 library(库),是树上活的树叶。灰色表示未引用代码,是秋天树上枯萎的树叶。为了除去死去的树叶,你必须摇动这棵树,使它们落下。

继续加油!ヾ(◍°∇°◍)ノ゙

猜你喜欢

转载自blog.csdn.net/qq_41887214/article/details/121922348