webpack 4 和 webpack5 Tree Shaking的区别

300 阅读2分钟

 一、先说V4

// src/math.js 
 export function square(x) {return x * x;}
 export function cube(x) {return x * x * x;}

// src/index 只引用了cube 
   import { cube } from './math.js'; 
   function component() {   
      var element = document.createElement('pre');
      element.innerHTML = [    
           'Hello webpack!', 
           '5 cubed is equal to ' + cube(5)
      ].join('\n\n');

   return element;
  }  
document.body.appendChild(component());

V4 生产模式下,build之后 tree-shaking的结果是只有cube,而没有square。

二、再说V5

1、解决了模块嵌套的 tree-shaking 问题

// inner.js
export const a = 1;
export const b = 2;

// module.js 
export * as inner from './inner';
// 或 import * as inner from './inner';
 export { inner };

// user.js
import * as module from './module';
console.log(module.inner.a);

在上面这个例子中,可以在生产模式下删除导出的b

2、内部模块 tree-shaking ——只导出但没有用到的也会被丢弃

Webpack 4 没有分析模块的导出和引用之间的依赖关系。webpack 5 有一个新的选项 optimization.innerGraph,在生产模式下是默认启用的,它可以对模块中的标志进行分析,找出导出和引用之间的依赖关系。

在这样的模块中:

import { something } from './something';

function usingSomething() {  return something;}

export function test() {  return usingSomething();}

内部依赖图算法会找出 something 只有在使用 test 导出时才会使用。这允许将更多的出口标记为未使用,并从代码包中省略更多的代码。也就说以上代码如果使用webpack4编译,something由于被引用了,它就一定不会被保留,不管之后的使用是否有效。

当配置里设置"sideEffects": false时,可以省略更多的模块。在这个例子中,当 test 导出未被使用时,./something 将被省略。(PS:这个属性得酌情小心使用,置为false,意味着生产环境下,项目没有被引用到的代码都会被丢弃,比如pollify,这个通常是没有导出的)

3、CommonJs Tree Shaking

在v4版本的时候官网明确说过 :

确保没有 compiler 将 ES2015 模块语法转换为 CommonJS 模块

 而在v5增加了一些支持,允许消除未使用的 CommonJs 导出,并从 require() **调用中跟踪引用的导出名称。注意:**不是百分百支持,以下是支持的:  

  • exports|this|module.exports.xxx = ...
  • exports|this|module.exports = require("...") (reexport)
  • exports|this|module.exports.xxx = require("...").xxx (reexport)
  • Object.defineProperty(exports|this|module.exports, "xxx", ...)
  • require("abc").xxx
  • require("abc").xxx()
  • 从 ESM 导入
  • require() 一个 ESM 模块
  • 被标记的导出类型 (对非严格 ESM 导入做特殊处理):
  • Object.defineProperty(exports|this|module.exports, "__esModule", { value: true|!0 })
  • exports|this|module.exports.__esModule = true|!0

PS:对于检测到的不可分析的代码时,webpack 会放弃,并且完全不跟踪这些模块的导出信息(出于性能考虑)。

完。