We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
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
什么是Tree Shaking?字面意思是摇树,一句话:项目中没有使用的代码会在打包时候丢掉。JS的Tree Shaking依赖的是ES2015的模块系统(比如:import和export)。
Tree Shaking
JS
ES2015
import和export
Tree Shaking可以用来剔除javascript中用不上的死代码。依赖静态的ES6模块化语法,例如通过import和export导入、导出。Tree Shaking最先在Rollup中出现,Webpack在2.0版本中将其引入。
javascript
ES6
Rollup
Webpack
2.0
为了更直观的理解它,来看一个具体的例子。假如有一个文件util.js里存放了很多工具函数和常量,在main.js中会导入和使用util.js,代码如下:
util.js
main.js
util.js源码:
export function funcA() { } export function funcB() { }
main.js源码:
import { funcA } from './util.js'; funcA();
Tree Shaking后的util.js:
export function funcA() { }
由于只用到了util.js中的funcA,所以剩下的都被Tree Shaking当作死代码给剔除了。
funcA
需要注意的是:要让Tree Shaking正常工作的前提是交给Webpack的 JavaScript代码必须是采用ES6模块化语法的,因为ES6模块化语法是静态的(导入导出语句中的路径必须是静态的字符串,而且不能放入其它代码块中),这让Webpack可以简单的分析出哪些export的被import过了。如果采用ES5中的模块化,例如module.export = {...}、require(x+y)、if(x){require('./util')},Webpack无法分析出哪些代码可以剔除。
JavaScript
export
import
ES5
module.export = {...}、require(x+y)、if(x){require('./util')}
上面讲了Tree Shaking是做什么的,接下来一步步教你如何配置Webpack让Tree Shaking生效。
首先,为了把采用ES6模块化的代码交给Webpack,需要配置Babel让其保留ES6模块化语句,修改.babelrc文件为如下:
Babel
.babelrc
{ "presets": [ [ "env", { "modules": false } ] ] }
其中"modules": false的含义是关闭Babel的模块转换功能,保留原本的 ES6模块化语法。
"modules": false
配置好Babel后,重新运行Webpack,在启动Webpack时带上--display-used-exports参数,以方便追踪Tree Shaking的工作, 这时你会发现在控制台中输出了如下的日志:
Webpac
--display-used-exports
> webpack --display-used-exports bundle.js 3.5 kB 0 [emitted] main [0] ./main.js 41 bytes {0} [built] [1] ./util.js 511 bytes {0} [built] [only some exports used: funcA]
其中[only some exports used: funcA]提示了util.js只导出了用到的funcA,说明Webpack确实正确的分析出了如何剔除死代码。
[only some exports used: funcA]
但当你打开Webpack输出的bundle.js文件看下时,你会发现用不上的代码还在里面,如下:
bundle.js
/* harmony export (immutable) */ __webpack_exports__["a"] = funcA; /* unused harmony export funB */ function funcA() { console.log('funcA'); } function funB() { console.log('funcB'); }
Webpack只是指出了哪些函数用上了哪些没用上,要剔除用不上的代码还得经过 UglifyJS去处理一遍。要接入UglifyJS也很简单,不仅可以通过UglifyJSPlugin插件去实现,也可以简单的通过在启动Webpack时带上--optimize-minimize参数,为了快速验证Tree Shaking,我们采用较简单的后者来实验下。
UglifyJS
UglifyJSPlugin
--optimize-minimize
通过webpack --display-used-exports --optimize-minimize重启 Webpack后,打开新输出的bundle.js,内容如下:
webpack --display-used-exports --optimize-minimize
function r() { console.log("funcA") } t.a = r
可以看出Tree Shaking确实做到了,用不上的代码都被剔除了。
当我们的项目使用了大量第三方库时,你会发现Tree Shaking似乎不生效了,原因是大部分Npm中的代码都是采用的CommonJS语法,这导致Tree Shaking无法正常工作而降级处理。但幸运的时有些库考虑到了这点,这些库在发布到Npm上时会同时提供两份代码,一份采用CommonJS模块化语法,一份采用 ES6模块化语法。并且在package.json文件中分别指出这两份代码的入口。
Npm
CommonJS
package.json
以redux库为例,其发布到 Npm 上的目录结构为:
redux
node_modules/redux |-- es | |-- index.js # 采用 ES6 模块化语法 |-- lib | |-- index.js # 采用 ES5 模块化语法 |-- package.json
package.json文件中有两个字段:
{ "main": "lib/index.js", // 指明采用 CommonJS 模块化的代码入口 "jsnext:main": "es/index.js" // 指明采用 ES6 模块化的代码入口 }
mainFields用于配置采用哪个字段作为模块的入口描述。为了让Tree Shaking对redux生效,需要配置Webpack的文件寻找规则为如下:
mainFields
module.exports = { resolve: { // 针对 Npm 中的第三方模块优先采用 jsnext:main 中指向的 ES6 模块化语法的文件 mainFields: ['jsnext:main', 'browser', 'main'] }, };
以上配置的含义是优先使用jsnext:main作为入口,如果不存在jsnext:main就采用browser或者main作为入口。虽然并不是每个Npm 中的第三方模块都会提供ES6模块化语法的代码,但对于提供了的不能放过,能优化的就优化。
jsnext:main
browser
main
目前越来越多的Npm中的第三方模块考虑到了Tree Shaking,并对其提供了支持。采用jsnext:main作为ES6模块化代码的入口是社区的一个约定,假如将来你要发布一个库到Npm时,希望你能支持Tree Shaking,以让Tree Shaking发挥更大的优化效果,让更多的人为此受益。
webpack4
需要注意的是:在webpack4中使用Tree Shaking,不再需要[uglifyjs-webpack-plugin]
The text was updated successfully, but these errors were encountered:
No branches or pull requests
1. 认识Tree
什么是
Tree Shaking
?字面意思是摇树,一句话:项目中没有使用的代码会在打包时候丢掉。JS
的Tree Shaking
依赖的是ES2015
的模块系统(比如:import和export
)。Tree Shaking
可以用来剔除javascript
中用不上的死代码。依赖静态的ES6
模块化语法,例如通过import和export
导入、导出。Tree Shaking
最先在Rollup
中出现,Webpack
在2.0
版本中将其引入。为了更直观的理解它,来看一个具体的例子。假如有一个文件
util.js
里存放了很多工具函数和常量,在main.js
中会导入和使用util.js
,代码如下:由于只用到了
util.js
中的funcA
,所以剩下的都被Tree Shaking
当作死代码给剔除了。2. 接入
Tree Shaking
上面讲了
Tree Shaking
是做什么的,接下来一步步教你如何配置Webpack
让Tree Shaking
生效。首先,为了把采用
ES6
模块化的代码交给Webpack
,需要配置Babel
让其保留ES6
模块化语句,修改.babelrc
文件为如下:其中
"modules": false
的含义是关闭Babel
的模块转换功能,保留原本的ES6
模块化语法。配置好
Babel
后,重新运行Webpac
k,在启动Webpack
时带上--display-used-exports
参数,以方便追踪Tree Shaking
的工作, 这时你会发现在控制台中输出了如下的日志:其中
[only some exports used: funcA]
提示了util.js
只导出了用到的funcA
,说明Webpack
确实正确的分析出了如何剔除死代码。但当你打开
Webpack
输出的bundle.js
文件看下时,你会发现用不上的代码还在里面,如下:Webpack
只是指出了哪些函数用上了哪些没用上,要剔除用不上的代码还得经过UglifyJS
去处理一遍。要接入UglifyJS
也很简单,不仅可以通过UglifyJSPlugin
插件去实现,也可以简单的通过在启动Webpack
时带上--optimize-minimize
参数,为了快速验证Tree Shaking
,我们采用较简单的后者来实验下。通过
webpack --display-used-exports --optimize-minimize
重启Webpack
后,打开新输出的bundle.js
,内容如下:可以看出
Tree Shaking
确实做到了,用不上的代码都被剔除了。当我们的项目使用了大量第三方库时,你会发现
Tree Shaking
似乎不生效了,原因是大部分Npm
中的代码都是采用的CommonJS
语法,这导致Tree Shaking
无法正常工作而降级处理。但幸运的时有些库考虑到了这点,这些库在发布到Npm
上时会同时提供两份代码,一份采用CommonJS
模块化语法,一份采用ES6
模块化语法。并且在package.json
文件中分别指出这两份代码的入口。以
redux
库为例,其发布到 Npm 上的目录结构为:mainFields
用于配置采用哪个字段作为模块的入口描述。为了让Tree Shaking
对redux
生效,需要配置Webpack
的文件寻找规则为如下:以上配置的含义是优先使用
jsnext:main
作为入口,如果不存在jsnext:main
就采用browser
或者main
作为入口。虽然并不是每个Npm
中的第三方模块都会提供ES6
模块化语法的代码,但对于提供了的不能放过,能优化的就优化。目前越来越多的
Npm
中的第三方模块考虑到了Tree Shaking
,并对其提供了支持。采用jsnext:main
作为ES6
模块化代码的入口是社区的一个约定,假如将来你要发布一个库到Npm
时,希望你能支持Tree Shaking
,以让Tree Shaking
发挥更大的优化效果,让更多的人为此受益。3.
webpack4
中使用Tree Shaking
需要注意的是:在
webpack4
中使用Tree Shaking
,不再需要[uglifyjs-webpack-plugin]参考博文
The text was updated successfully, but these errors were encountered: