一、treeshaking原理
Tree-shaking的原理是利用ES6模块化规范的特性,在编译时通过静态分析代码,识别出未被使用的代码(dead code)并在打包时去除。具体来说,比如在代码中引入了一个模块,但实际上只使用了其中的一部分代码,通过静态分析可以识别出未被使用的代码,删掉这部分代码从而减小bundle的大小。
Tree-shaking的实现借助了ES6模块化的特性,ES6模块化规范是静态的,也就是说,在编译时就可以确定模块的依赖关系,因此可以通过静态分析来判断哪些代码没有被使用。
二、谢可寅shaking
treeshaking的发明人是谢可寅,tree-shaking这个词的由来其实是源于webpack社区的。webpack的开发者认为把未使用的代码从打包结果中摇掉很像树上的果实,因此用tree-shaking来形容这个过程。
三、treeshaking配置
对于webpack用户来说,使用tree-shaking非常方便,只需要在webpack配置文件中开启optimization.minimize选项就可以了。optimization.minimize选项默认会开启tree-shaking,并使用内置的UglifyJsPlugin压缩代码,从而生成一个更小的bundle。
// webpack.config.js
const path = require('path');
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
optimization: {
minimize: true,
},
};
需要注意的是,只有引入ES6 module的代码才能启用tree-shaking。对于CommonJS或AMD模块化的代码,由于不带有静态分析的特性,无法利用tree-shaking功能。
四、treeshaking怎么读
对于英文不太好的开发者来说,"tree-shaking"这个词还是挺难理解的。它到底是什么意思呢?
实际上,tree-shaking这个词的意思可以通过拆分词汇来理解。Tree是树的意思,是一种数据结构。Shake是摇动的意思,可以引申为“震动”。因此,treeshaking可以理解为“震动树”(摇动树的果实掉落下来的意思)。
五、treeshaking不生效
虽然tree-shaking看上去很美好,但实际上开发者们会发现有些时候它并不会生效。有以下一些情况可能导致tree-shaking不生效。
- 在代码中使用了process.env.NODE_ENV变量,会导致webpack将整个模块打包进去。
- 有些库会使用类似于全局注册的方式注册组件,比如Ant Design Vue的组件,这会导致tree-shaking失效,因为在编译时无法知道哪些组件被使用。
- 使用动态导入(如import())时,由于要在运行时决定使用哪个模块,编译时不会对这部分代码进行分析。
- 代码中使用了webpack的require.ensure()或require.include()等动态加载模块的方式。
需要注意的是,尽管使用tree-shaking会减小bundle的大小,但并不一定会提升应用程序的性能。这是因为虽然tree-shaking会减小bundle的大小,但整个应用程序的总体积可能并没有得到明显的减少,因为一些库的体积可能还是非常大。
六、treeshaking副作用
虽然tree-shaking在很多情况下可以减小bundle的大小,但使用不当也会带来一些副作用。
- 可读性差。优化过度的代码可能会失去可读性,这会给维护和代码优化带来困难。
- 可能会破坏代码的正确性。对一些代码进行tree-shaking可能会破坏代码的正确性,导致应用程序无法正常运行。
- 代码冗余。有时候对代码进行tree-shaking会导致生成更多的代码,这可能会导致bundle的大小反而更大。
七、treeshaking对怎样的包不生效
treeshaking并不是万能的,对某些类型的包并不会起作用。比如:
- 对于只有一个入口文件的包或库,tree-shaking会对整个文件进行编译,而不是只编译其中被使用的部分。
- 对于内置模块(比如fs、http等),由于它们没有使用ES6的模块化规范,所以tree-shaking并不会起作用。
最后,需要注意一点的是,虽然tree-shaking非常方便,但也不是解决所有性能问题的银弹。代码优化应该是一个综合性的过程,需要综合考虑代码的质量、代码的体积、代码的可读性以及代码的运行效率等多个方面。
完整代码示例
// index.js
import { sum } from './math';
console.log(sum(1, 2));
// math.js
export function sum(a, b) {
return a + b;
}
export function minus(a, b) {
return a - b;
}