Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webpack学习笔记(8)-利用tree-shaking剔除冗余代码 #25

Closed
sevenCon opened this issue Oct 17, 2019 · 0 comments
Closed

webpack学习笔记(8)-利用tree-shaking剔除冗余代码 #25

sevenCon opened this issue Oct 17, 2019 · 0 comments
Labels

Comments

@sevenCon
Copy link
Owner

前言

tree-shaking, 字面的上意思是摇晃树,用来在ES Module中, 剔除不需要引入的模块的, 这是一个从webpack2.0`的时候就支持的功能, 但是支持的程度不尽人意.

具体的问题是babel, uglifyJs会有副作用,具体请看你的Tree-Shaking并没什么卵用, 非常nice的一篇文章.

文章里面的想表达的意思, 因为在babel和uglifyJs的打包之后, webpack很难判断依赖的代码或者项目的代码,是否具有副作用, 或者说打包后都产生了副作用,副作用包括了属性的获取,对象属性的编辑, 或其他影响了全局变量或者方法的操作. 比如赋值属性obj2 = obj1; obj2.prototype.toString=fn等等.因为修改了obj1的方法,这就是会产生副作用. babel实现一个PolyfillClass, 会给这个变量使用defineProperty定义属性,这也算是副作用的一种.

正是以上的原因,因为产生了副作用, 所以导致了webpack用的uglifyJs打包出来的效果和我们相差甚远.

因此, 在webpack4.0之后, 使用ES Module的terser-webpack-plugin进行打包, terser-webpack-plugin包其实uglifyJs-es的一个fork, 支持ES Module 语法的压缩处理.

sideEffects:false 的打包效果

下面是sideEffects:false的打包效果, 以webpack2.x/webpack3.x和 webpack4.x的打包版本进行对比.

在webpack2.x, webpack3.x中使用的是环境

// webpack 2.x
"dependencies": {
    "babel-loader": "6.2",
    "uglifyjs-webpack-plugin": "1.1.1",
    "webpack": "2.4",
    "babel-core": "^6.26.3",
    "babel-preset-es2015": "^6.24.1",
    "webpack-cli": "^3.3.9"
}
  
// webpack 3.x
"dependencies": {
    "babel-core": "^6.26.3",
    "babel-loader": "7",
    "babel-preset-env": "^1.7.0",
    "uglifyjs-webpack-plugin": "1",
    "webpack": "3"
}
/* index.js */
import { B } from './test';
const b = new B('bbbbbbbbbbbb').getName();
console.log(b);


// test.js
export class A {
  constructor() {
    this.name = 'AAAAAAAAAAAAAA';
  }
  getName() {
    return this.name;
  }
}

export class B {
  constructor({ val }) {
    this.name = 'constructor bbbbbbbbbbb';
    this.val = val;
  }
  getName() {
    console.log(this.val);
    return this.val;
  }
}

tree-shaking如果是理想的话, 可以修剪掉test.js#class A.

另外注意的是:webpack2.x/webpack3.x使用tree-shaking是有条件的

  • 使用uglifyjs打包压缩
  • 设置babel的preset-env#module:false, 设置忽略ES Module代码
  • 使用import {xxx} from xxx的方式加载模块代码
    说白了还是上面的谈到的问题, tree-shaking完全由uglifyJS来做, 那么他对副作用的判断和决定什么时候进行tree-shaking, 是说不准的, 而在babel打包出来的内容本身就具有副作用的.

所以因为这个问题, uglifyJs有个issue讨论了添加tree-shaking的副作用, 所有出现了/@PURE/.

webpack2.x,webpack3.x

webpack4.x

用webpack4.x打包出来的就已经tree-shaking掉了不用到的类class A, 而同样的配置, 在webpack2.x, webpack3.x中是效果不大的, 特别在IIFE的情况下, uglifyJs的tree-shaking难以工作.

optimize#sideEffects

{
  optimization: {
    sideEffects: true | false, 
    /*生产模式下默认开启,其他模式不开启。
      如果开启, 那么就会以package.json#sideEffect里面的内容为参考对象*/
  }
}

package.json#sideEffect的值true|false|[ignore files], 默认为false, 在项目开启tree-shaking的时候,常常需要把这个值设置为忽略我们的sass 或者less的代码, 否则会影响打包的css结果.

比如:

{
    "sideEffects":['./sass/index.scss']
}

指定特定的文件具有副作用, 进而忽略对应文件的tree-shaking, 如果想要关注更多的tree-shaking打包细节,请看terser.

总结

tree-shaking是代码优化中的有效而且重要的一环, 今天的文章算是一个抛转引玉, 期待社区出现关于terser解读tree-shaking的深度好文.

参考

https://github.com/webpack/webpack/tree/master/examples/side-effects
https://juejin.im/post/5b4ff9ece51d45190c18bb65
https://segmentfault.com/q/1010000018871965
https://zhuanlan.zhihu.com/p/32831172

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant