From c4d41d34c2b9ca7097b6e61c6934a31fcaca963b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=9B=E5=AE=9A=E8=B0=94=E7=9A=84=E7=8C=AB?= Date: Thu, 17 Aug 2017 19:30:12 +0800 Subject: [PATCH 1/6] =?UTF-8?q?=E8=AF=91=EF=BC=9A=E5=A6=82=E4=BD=95?= =?UTF-8?q?=E5=9C=A8webpack2=E4=B8=AD=E4=BD=BF=E7=94=A8tree-shaking?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...-to-do-proper-tree-shaking-in-webpack-2.md | 86 +++++++++---------- 1 file changed, 42 insertions(+), 44 deletions(-) diff --git a/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md b/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md index b61f8a1e673..dc5b077f511 100644 --- a/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md +++ b/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md @@ -3,20 +3,20 @@ > * 原文作者:[Gábor Soós](https://blog.craftlab.hu/@blacksonic86) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md](https://github.com/xitu/gold-miner/blob/master/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md) - > * 译者: + > * 译者:[薛定谔的猫](https://github.com/Aladdin-ADD/) > * 校对者: - # How to do proper tree-shaking in Webpack 2 + # 如何在 Webpack 2 中使用 tree-shaking - The term tree-shaking was first introduced by Rich Harris’ module bundler, [Rollup](https://rollupjs.org/). Tree-shaking means that Javascript bundling will only include code that is necessary to run your application. It has been made available by the static nature of ES2015 modules (exports and imports can’t be modified at runtime), which lets us detect unused code at bundle time. This feature has become available to Webpack users with the second version. Now [Webpack](https://webpack.js.org/) has built-in support for ES2015 modules and tree-shaking. In this tutorial I’ll show you how tree-shaking works in Webpack and how to overcome the obstacles that come our way. +tree-shaking 这个术语首先源自 [Rollup](https://rollupjs.org/) -- Rich Harris 写的模块打包工具。它是指在打包时只包含用到的 Javascript 代码。它的可行性基于 ES6 的静态模块(exports 和 imports 不能在运行时修改),这使我们在打包时可以检测到未使用的代码。Webpack 2 也引入了这一特性,[Webpack 2](https://webpack.js.org/) 已经内置支持 ES6 模块和 tree-shaking。本文将会介绍如何在 webpack 中使用这一特性,如何克服使用中的难点。 ![](https://cdn-images-1.medium.com/max/2000/1*djuJdyxfBwGEClfgji8GRw.jpeg) -If you just want to skip to the working examples visit my [Babel](https://github.com/blacksonic/babel-webpack-tree-shaking) or [Typescript](https://github.com/blacksonic/typescript-webpack-tree-shaking) repository. +如果想跳过,直接看例子请访问 [Babel](https://github.com/blacksonic/babel-webpack-tree-shaking)、[Typescript](https://github.com/blacksonic/typescript-webpack-tree-shaking)。 -#### Example application +#### 应用举例 -The way tree-shaking works in Webpack can be best shown through a minimalistic example. I’ll compare it to a car that has a specific engine. The application consists of two files. The first one holds the different engines as classes and their version as a function. Every class and function is exported from its file. +理解在 Webpack 中使用 tree-shaking 的最佳的方式是通过一个微型应用例子。我将它比作一个汽车有特定的引擎,该应用由 2 个文件组成。第 1 个文件有:一些 class,代表不同种类的引擎;一个函数返回其版本号 -- 都通过 export 关键字导出。 ``` export class V6Engine { @@ -36,7 +36,7 @@ export function getVersion() { } ``` -The next file describes the car with its engine and serves as the entry point for our application. We will start the bundling from this file. +第 2 个文件表示一个汽车拥有它自己的引擎,将这个文件作为应用打包的入口(entry)。 ``` import { V8Engine } from './engine'; @@ -54,16 +54,15 @@ class SportsCar { console.log(new SportsCar(new V8Engine()).toString()); ``` -After defining the car class, we only use the *V8Engine* class, the other exports remain untouched. When running the application it will output *‘V8 Sports Car’*. +通过定义类 SportsCar,我们只使用了 *V8Engine*,而没有用到 *V6Engine*。运行这个应用会输出:*‘V8 Sports Car’*。 -With tree-shaking in place we expect the output bundle to only include classes and functions we use. In our case it means the *V8Engine* and the *SportsCar* class only. Let’s see how it works under the hood. +应用了 tree-shaking 后,我们期望输出包只包含用到的类和函数。在这个例子中,意味着它只有* V8Engine *和* SportsCar *类。让我们来看看它是如何工作的。 -#### Bundling +#### 打包 ![](https://cdn-images-1.medium.com/max/1600/1*eXdX_sQKzEZomscFgpEwRQ.png) -When we bundle the application without transformations (like [Babel](https://babeljs.io/)) and minification (like [UglifyJS](https://github.com/mishoo/UglifyJS2)), we will get the following output: - +我们打包时不使用变换([Babel](https://babeljs.io/)等)和压缩([UglifyJS](https://github.com/mishoo/UglifyJS2)等),可以得到如下输出: ``` (function(module, __webpack_exports__, __webpack_require__) { @@ -110,23 +109,23 @@ console.log(new SportsCar(new __WEBPACK_IMPORTED_MODULE_0__engine__["a" /* V8Eng /***/ }) ``` -Webpack marks classes and functions with comments which are not used (*/* unused harmony export V6Engine */*) and only exports those which are used (*/* harmony export (immutable) */ __webpack_exports__[“a”] = V8Engine;*). The very first question you may ask is that why is the unused code still there? Tree-shaking isn’t working, is it? +Webpack 用注释*/* unused harmony export V6Engine */*将未使用的类和函数标记下来,用*/* harmony export (immutable) */ __webpack_exports__[“a”] = V8Engine;*来标记用到的。你自然会问未使用的代码怎么还在?tree-shaking 没有生效吗? #### Dead code elimination vs live code inclusion -The reason behind this is that Webpack only marks code unused and doesn’t export it inside the module. It pulls in all of the available code and leaves dead code elimination to minification libraries like UglifyJS. UglifyJS gets the bundled code and removes unused functions and variables before minifying. With this mechanism it should remove the *getVersion* function and the *V6Engine* class. +背后的原因是:Webpack 仅仅标记未使用的代码(而不移除),并且不将其导出到模块外。它拉取所有用到的代码,将剩余的(未使用的)代码留给像 UglifyJS 这类压缩代码的工具来移除。UglifyJS 读取打包结果,在压缩之前移除未使用的代码。通过这一机制,就可以移除未使用的函数 *getVersion* 和类 *V6Engine*。 -Rollup, on the other hand, only includes the code that is necessary to run the application. When bundling is done, there are no unused classes and functions. Minification only deals with the actually used code. +而Rollup不同,它(的打包)只包含运行应用程序所必需的代码。打包完成后的输出并没有未使用的类和函数,压缩仅涉及实际使用的代码。 -#### Setting it up +#### 设置 -UglifyJS [doesn’t support the new language features of Javascript](https://github.com/mishoo/UglifyJS2/issues/448) (aka ES2015 and above) yet. We need Babel to transpile our code to ES5 and then use UglifyJS to clean up the unused code. +UglifyJS [不支持 ES6](https://github.com/mishoo/UglifyJS2/issues/448)(又名 ES2015,及以上)。 我们需要用 Babel 将代码编译为 ES5,然后用UglifyJS 来清除无用代码。 ![](https://cdn-images-1.medium.com/max/1600/1*FS50WgvWgoi3hxY_IPqTXw.png) -The most important thing is to leave ES2015 modules untouched by Babel presets. Webpack understands harmony modules and can only find out what to tree-shake if modules are left in their original format. If we transpile them also to CommonJS syntax, Webpack won’t be able to determine what is used and what is not. In the end Webpack will translate them to CommonJS syntax. +最重要的是让 ES6模块 不受 Babel 预设(preset)的影响。Webpack 认识 ES6 模块,只有当保留 ES6 模块语法时才能够应用 tree-shaking。如果将其转换为 CommonJS 语法,Webpack不知道哪些代码是使用过的,哪些不是(就不能应用 tree-shaking了)。最后,Webpack将把它们转换为 CommonJS 语法。 -We have to tell the preset (in our case [babel-preset-env](https://github.com/babel/babel-preset-env)) to skip the module transpilation. +我们需要告诉 Babel preset(在这个例子中是[babel-preset-env](https://github.com/babel/babel-preset-env))不要转换 module。 ``` { @@ -139,7 +138,7 @@ We have to tell the preset (in our case [babel-preset-env](https://github.com/ba } ``` -The corresponding Webpack config part. +对应 Webpack 配置: ``` module: { @@ -165,20 +164,19 @@ plugins: [ ] ``` -Let’s look at the output what we got after tree-shaking: [link to minified code](https://github.com/blacksonic/babel-webpack-tree-shaking/blob/master/dist/car.prod.bundle.js). +来看一下 tree-shaking 之后的输出: [link to minified code](https://github.com/blacksonic/babel-webpack-tree-shaking/blob/master/dist/car.prod.bundle.js). -We see the getVersion function removed as expected, but the V6Engine class remained there in the minified code. What can be the problem, what went wrong? +可以看到函数 getVersion 被移除了,这是我们所预期的,然而类 V6Engine 并没有被移除。这是什么原因呢? #### Troubles ahead -First Babel detects the ES2015 class and transpiles it down to it’s ES5 equivalent. Then comes Webpack by putting the modules together and in the end UglifyJS removes unused code. We can read what is the exact problem from the output of UglifyJS. +首先 Babel 检测到 ES6 模块将其转换为 ES5,然后 Webpack 将所有的模块聚集起来,最后 UglifyJS 会移除未使用的代码。我们来看一下 UglifyJS 的输出,就可以找到问题出在哪里。 *WARNING in car.prod.bundle.js from UglifyJs Dropping unused function getVersion [car.prod.bundle.js:103,9] Side effects in initialization of unused variable V6Engine [car.prod.bundle.js:79,4]* -It tells us that the ES5 equivalent of the *V6Engine* class has side effects at initialization. - +它告诉我们类 *V6Engine* 转换为 ES5的代码在初始化时有副作用。 ``` var V6Engine = function () { function V6Engine() { @@ -193,19 +191,19 @@ var V6Engine = function () { }(); ``` -When we define classes in ES5, class methods have to be assigned to the *prototype* property. There is no way around skipping at least one assignment. UglifyJS can’t tell if it is just a class declaration or some random code with side effects, because it can’t do control flow analysis. +在使用 ES5 语法定义类时,类的成员函数会被添加到属性 *prototype*,没有什么方法能完全避免这次赋值。UglifyJS 不能够分辨它仅仅是类声明,还是其它有副作用的操作 -- UglifyJS 不能做控制流分析。 -Transpiled code breaks the tree-shaking of classes. It only works for functions out of the box. +编译过程阻止了对类进行 tree-shaking。它仅对函数起作用。 -There are multiple on-going bug reports related to this on Github in the [Webpack repository](https://github.com/webpack/webpack/issues/2867) and in the [UglifyJS repository](https://github.com/mishoo/UglifyJS2/issues/1261). One solution can be to complete the ES2015 support in UglifyJS. Hopefully it will be released with the [next major version](https://github.com/mishoo/UglifyJS2/issues/1411). Another solution can be to implement an annotation for downleveled classes that mark it as pure (side effect free) for UglifyJS. This way UglifyJS can be sure that this declaration has no side effects. Its support is [already implemented](https://github.com/mishoo/UglifyJS2/pull/1448) but to make it work, transpilers have to support it and emit the @__PURE__ annotation next to the downleveled class. There are ongoing issues implementing this behavior in [Babel](https://github.com/babel/babel/issues/5632) and [Typescript](https://github.com/Microsoft/TypeScript/issues/13721). +在 Github 上,有一些相关的 bug report:[Webpack repository](https://github.com/webpack/webpack/issues/2867)、[UglifyJS repository](https://github.com/mishoo/UglifyJS2/issues/1261)。一个解决方案是 UglifyJS 完全支持 ES6,希望[下个主版本](https://github.com/mishoo/UglifyJS2/issues/1411)能够支持。另一个解决方案是将其标记为 pure(无副作用),以便 UglifyJS 能够处理。这种方法[已经实现](https://github.com/mishoo/UglifyJS2/pull/1448),但要想生效,还需编译器支持将类编译后的赋值标记为 @__PURE__。实现进度:[Babel](https://github.com/babel/babel/issues/5632)、[Typescript](https://github.com/Microsoft/TypeScript/issues/13721)。 -#### Babili to the rescue +#### 使用 Babili -The developers behind Babel thought why not make a minifier based on Babel that understands ES2015 and above? They created [Babili](https://github.com/babel/babili), which can understand every new language feature that Babel can parse. Babili can transpile ES2015 code into ES5 code and minify it including removal of unused classes and functions. Just like UglifyJS would have already implemented ES2015 support with the addition that it will automatically catch up with the new language features. +Babel 的开发者们认为:何不开发一个基于 Babel 的代码压缩工具,就能够识别 ES6+ 的语法了。所以他们开发了[Babili](https://github.com/babel/babili),所有 Babel 可以解析的语言特性它都支持。Babili 能将 ES6 代码编译为 ES5,移除未使用的类和函数。就像 UglifyJS 已经支持 ES6 一样。 -Babili will remove unused code before transpilation. It is much easier to spot unused classes before downleveled to ES5. Tree-shaking will also work for class declarations, not just functions. +Babili 会在编译前删除未使用的代码。在编译为 ES5 之前,很容易找到未使用的类,因此 tree-shaking 也可以用于类声明,而不再仅仅是函数。 -We only have to replace the UglifyJS plugin with the Babili plugin and remove the loader for Babel. The other way around is to use Babili as a Babel preset and use only the loader. I would recommend using the plugin, because it can also work when we are using a transpiler that is not Babel (for example Typescript). +我们只需用 Babili 替换 UglifyJS,然后删除 babel-loader 即可。另一种方式是将 Babili 作为 Babel 的预设,仅使用 babel-loader(移除 UglifyJS 插件)。推荐使用第一种(插件的方式),因为当编译器不是 Babel(比如 Typescript)时,也能生效。 ``` module: { @@ -217,27 +215,27 @@ plugins: [ ] ``` -We always have to pass ES2015+ code down to the plugin, otherwise it won’t be able to remove classes. +我们需要将 ES6+ 代码传给 BabiliPlugin,否则它不用移除(未使用的)类。 -ES2015+ is also important when using other transpilers like Typescript. Typescript has to output ES2015+ code and harmony modules to enable tree-shaking. The output of Typescript will be handed over to Babili to remove the unused code. +使用 Typescript 等编译器时,也应当使用 ES6+。Typescript应当输出 ES6+ 代码,以便 tree-shaking 能够生效。 -The output now won’t contain the class *V6Engine*: [link to minified code](https://github.com/blacksonic/babel-webpack-tree-shaking/blob/master/dist/car.es2015.prod.bundle.js). +现在的输出不再包含类 *V6Engine*:[压缩后代码](https://github.com/blacksonic/babel-webpack-tree-shaking/blob/master/dist/car.es2015.prod.bundle.js)。 -#### Libraries +#### 第三方包 -The same rules apply for libraries as for our code. It should use the ES2015 modules format. Luckily more and more library authors release their packages in both CommonJS style format and the new module format. The entry point for the new module format is marked with the *module* field in *package.json*. +对第三方包来说也是,应当使用 ES6 模块。幸运的是,越来越多的包作者同时发布 CommonJS 格式 和 ES6 格式的模块。ES6 模块的入口由 *package.json* 的字段 *module* 指定。 -With the new module format unused functions will be removed, but for classes it is not enough. The library classes also have to be in ES2015 format to be removable by Babili. It is very rare that libraries are published in this format, but for some it is available (for example lodash as lodash-es). +对 ES6 模块,未使用的函数会被移除,但 class 并不一定会。只有当包内的 calss 定义也为 ES6 格式时,Babili 才能移除。很少有包能够以这种格式发布,但有的做到了(比如说 lodash 的 lodash-es)。 -One last culprit can be when the separate files of the library modify other modules by extending them; importing files have side effects. The operators of [RxJs](https://github.com/Reactive-Extensions/RxJS) is good example for this. By importing an operator it modifies one of the classes. These are considered side effects and they stop the code from being tree-shaken. +罪魁祸首是当包的单独文件通过扩展它们来修改其他模块时,导入文件有副作用。[RxJs](https://github.com/Reactive-Extensions/RxJS)就是一个例子。通过导入一个运算符来修改其中一个类,这些被认为是副作用,它们阻止代码进行 tree-shaking。 -#### Summary +#### 总结 -With tree-shaking you can bring down the size of your application considerable. Webpack 2 has built-in support for it, but works differently from Rollup. It will include everything but will mark unused functions and classes, leaving the actual code removal to minifiers. This is what makes it a bit more difficult for us to tree-shake everything. Going with the default minifier, UglifyJS, it will remove only unused functions and variables. To remove classes also, we have to use Babili which, understands ES2015 classes. We also have to pay special attention to modules, whether they are published in a way that supports tree-shaking. +通过 tree-shaking 你可以相当程度上减少应用的体积。Webpack 2 内置支持它,但其机制并不同于 Rollup。它会包含所有的代码,标记未使用的函数和函数,以便压缩工具能够移除。这就是对所有代码都进行 tree-shake 的困难之处。使用默认的压缩工具 UglifyJS,它仅移除未使用的函数和变量;Babili 支持 ES6,可以用它来移除类。我们还必须特别注意第三方模块发布的方式是否支持 tree-shaking。 -I hope this article clarifies the inner workings behind Webpack’s tree-shaking and gives you ideas to overcome the obstacles. +希望这篇文章澄清了 Webpack 的 tree-shaking 背后的原理,并为您提供了克服困难的思路。 -You can see the working examples in my [Babel](https://github.com/blacksonic/babel-webpack-tree-shaking) and [Typescript](https://github.com/blacksonic/typescript-webpack-tree-shaking) repository. +实际例子请访问 [Babel](https://github.com/blacksonic/babel-webpack-tree-shaking)、[Typescript](https://github.com/blacksonic/typescript-webpack-tree-shaking)。 --- @@ -247,4 +245,4 @@ You can see the working examples in my [Babel](https://github.com/blacksonic/bab --- > [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[React](https://github.com/xitu/gold-miner#react)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计) 等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 - \ No newline at end of file + From 2b324677ed7d6a4bb87cb4bd4cc6361aa392904f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=9B=E5=AE=9A=E8=B0=94=E7=9A=84=E7=8C=AB?= Date: Fri, 18 Aug 2017 21:26:47 +0800 Subject: [PATCH 2/6] Fix: accept review suggestions. --- ...-to-do-proper-tree-shaking-in-webpack-2.md | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md b/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md index dc5b077f511..4891bb8fc57 100644 --- a/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md +++ b/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md @@ -8,7 +8,7 @@ # 如何在 Webpack 2 中使用 tree-shaking -tree-shaking 这个术语首先源自 [Rollup](https://rollupjs.org/) -- Rich Harris 写的模块打包工具。它是指在打包时只包含用到的 Javascript 代码。它的可行性基于 ES6 的静态模块(exports 和 imports 不能在运行时修改),这使我们在打包时可以检测到未使用的代码。Webpack 2 也引入了这一特性,[Webpack 2](https://webpack.js.org/) 已经内置支持 ES6 模块和 tree-shaking。本文将会介绍如何在 webpack 中使用这一特性,如何克服使用中的难点。 +tree-shaking 这个术语首先源自 [Rollup](https://rollupjs.org/) -- Rich Harris 写的模块打包工具。它是指在打包时只包含用到的 Javascript 代码。它依赖于 ES6 静态模块(exports 和 imports 不能在运行时修改),这使我们在打包时可以检测到未使用的代码。Webpack 2 也引入了这一特性,[Webpack 2](https://webpack.js.org/) 已经内置支持 ES6 模块和 tree-shaking。本文将会介绍如何在 webpack 中使用这一特性,如何克服使用中的难点。 ![](https://cdn-images-1.medium.com/max/2000/1*djuJdyxfBwGEClfgji8GRw.jpeg) @@ -56,13 +56,13 @@ console.log(new SportsCar(new V8Engine()).toString()); 通过定义类 SportsCar,我们只使用了 *V8Engine*,而没有用到 *V6Engine*。运行这个应用会输出:*‘V8 Sports Car’*。 -应用了 tree-shaking 后,我们期望输出包只包含用到的类和函数。在这个例子中,意味着它只有* V8Engine *和* SportsCar *类。让我们来看看它是如何工作的。 +应用了 tree-shaking 后,我们期望打包结果只包含用到的类和函数。在这个例子中,意味着它只有 *V8Engine* 和 *SportsCar* 类。让我们来看看它是如何工作的。 #### 打包 ![](https://cdn-images-1.medium.com/max/1600/1*eXdX_sQKzEZomscFgpEwRQ.png) -我们打包时不使用变换([Babel](https://babeljs.io/)等)和压缩([UglifyJS](https://github.com/mishoo/UglifyJS2)等),可以得到如下输出: +我们打包时不使用编译器([Babel](https://babeljs.io/) 等)和压缩工具([UglifyJS](https://github.com/mishoo/UglifyJS2) 等),可以得到如下输出: ``` (function(module, __webpack_exports__, __webpack_require__) { @@ -109,23 +109,23 @@ console.log(new SportsCar(new __WEBPACK_IMPORTED_MODULE_0__engine__["a" /* V8Eng /***/ }) ``` -Webpack 用注释*/* unused harmony export V6Engine */*将未使用的类和函数标记下来,用*/* harmony export (immutable) */ __webpack_exports__[“a”] = V8Engine;*来标记用到的。你自然会问未使用的代码怎么还在?tree-shaking 没有生效吗? +Webpack 用注释 */\*unused harmony export V6Engine\*/* 将未使用的类和函数标记下来,用 */\*harmony export (immutable)\*/ __webpack_exports__[“a”] = V8Engine;* 来标记用到的。你应该会问未使用的代码怎么还在?tree-shaking 没有生效吗? -#### Dead code elimination vs live code inclusion +#### 移除未使用代码(Dead code elimination) vs 包含已使用代码(live code inclusion) 背后的原因是:Webpack 仅仅标记未使用的代码(而不移除),并且不将其导出到模块外。它拉取所有用到的代码,将剩余的(未使用的)代码留给像 UglifyJS 这类压缩代码的工具来移除。UglifyJS 读取打包结果,在压缩之前移除未使用的代码。通过这一机制,就可以移除未使用的函数 *getVersion* 和类 *V6Engine*。 -而Rollup不同,它(的打包)只包含运行应用程序所必需的代码。打包完成后的输出并没有未使用的类和函数,压缩仅涉及实际使用的代码。 +而 Rollup 不同,它(的打包结果)只包含运行应用程序所必需的代码。打包完成后的输出并没有未使用的类和函数,压缩仅涉及实际使用的代码。 #### 设置 -UglifyJS [不支持 ES6](https://github.com/mishoo/UglifyJS2/issues/448)(又名 ES2015,及以上)。 我们需要用 Babel 将代码编译为 ES5,然后用UglifyJS 来清除无用代码。 +UglifyJS [不支持 ES6](https://github.com/mishoo/UglifyJS2/issues/448)(又名 ES2015)及以上。我们需要用 Babel 将代码编译为 ES5,然后再用 UglifyJS 来清除无用代码。 ![](https://cdn-images-1.medium.com/max/1600/1*FS50WgvWgoi3hxY_IPqTXw.png) -最重要的是让 ES6模块 不受 Babel 预设(preset)的影响。Webpack 认识 ES6 模块,只有当保留 ES6 模块语法时才能够应用 tree-shaking。如果将其转换为 CommonJS 语法,Webpack不知道哪些代码是使用过的,哪些不是(就不能应用 tree-shaking了)。最后,Webpack将把它们转换为 CommonJS 语法。 +最重要的是让 ES6 模块不受 Babel 预设(preset)的影响。Webpack 认识 ES6 模块,只有当保留 ES6 模块语法时才能够应用 tree-shaking。如果将其转换为 CommonJS 语法,Webpack 不知道哪些代码是使用过的,哪些不是(就不能应用 tree-shaking了)。最后,Webpack将把它们转换为 CommonJS 语法。 -我们需要告诉 Babel preset(在这个例子中是[babel-preset-env](https://github.com/babel/babel-preset-env))不要转换 module。 +我们需要告诉 Babel 预设(在这个例子中是[babel-preset-env](https://github.com/babel/babel-preset-env))不要转换 module。 ``` { @@ -168,7 +168,7 @@ plugins: [ 可以看到函数 getVersion 被移除了,这是我们所预期的,然而类 V6Engine 并没有被移除。这是什么原因呢? -#### Troubles ahead +#### 问题 首先 Babel 检测到 ES6 模块将其转换为 ES5,然后 Webpack 将所有的模块聚集起来,最后 UglifyJS 会移除未使用的代码。我们来看一下 UglifyJS 的输出,就可以找到问题出在哪里。 @@ -176,7 +176,7 @@ plugins: [ Dropping unused function getVersion [car.prod.bundle.js:103,9] Side effects in initialization of unused variable V6Engine [car.prod.bundle.js:79,4]* -它告诉我们类 *V6Engine* 转换为 ES5的代码在初始化时有副作用。 +它告诉我们类 *V6Engine* 转换为 ES5 的代码在初始化时有副作用。 ``` var V6Engine = function () { function V6Engine() { @@ -195,15 +195,15 @@ var V6Engine = function () { 编译过程阻止了对类进行 tree-shaking。它仅对函数起作用。 -在 Github 上,有一些相关的 bug report:[Webpack repository](https://github.com/webpack/webpack/issues/2867)、[UglifyJS repository](https://github.com/mishoo/UglifyJS2/issues/1261)。一个解决方案是 UglifyJS 完全支持 ES6,希望[下个主版本](https://github.com/mishoo/UglifyJS2/issues/1411)能够支持。另一个解决方案是将其标记为 pure(无副作用),以便 UglifyJS 能够处理。这种方法[已经实现](https://github.com/mishoo/UglifyJS2/pull/1448),但要想生效,还需编译器支持将类编译后的赋值标记为 @__PURE__。实现进度:[Babel](https://github.com/babel/babel/issues/5632)、[Typescript](https://github.com/Microsoft/TypeScript/issues/13721)。 +在 Github 上,有一些相关的 bug report:[Webpack repository](https://github.com/webpack/webpack/issues/2867)、[UglifyJS repository](https://github.com/mishoo/UglifyJS2/issues/1261)。一个解决方案是 UglifyJS 完全支持 ES6,希望[下个主版本](https://github.com/mishoo/UglifyJS2/issues/1411)能够支持。另一个解决方案是将其标记为 pure(无副作用),以便 UglifyJS 能够处理。这种方法[已经实现](https://github.com/mishoo/UglifyJS2/pull/1448),但要想生效,还需编译器支持将类编译后的赋值标记为 @\__PURE\__。实现进度:[Babel](https://github.com/babel/babel/issues/5632)、[Typescript](https://github.com/Microsoft/TypeScript/issues/13721)。 #### 使用 Babili -Babel 的开发者们认为:何不开发一个基于 Babel 的代码压缩工具,就能够识别 ES6+ 的语法了。所以他们开发了[Babili](https://github.com/babel/babili),所有 Babel 可以解析的语言特性它都支持。Babili 能将 ES6 代码编译为 ES5,移除未使用的类和函数。就像 UglifyJS 已经支持 ES6 一样。 +Babel 的开发者们认为:为什么不开发一个基于 Babel 的代码压缩工具,这样就能够识别 ES6+ 的语法了。所以他们开发了[Babili](https://github.com/babel/babili),所有 Babel 可以解析的语言特性它都支持。Babili 能将 ES6 代码编译为 ES5,移除未使用的类和函数,这就像 UglifyJS 已经支持 ES6 一样。 Babili 会在编译前删除未使用的代码。在编译为 ES5 之前,很容易找到未使用的类,因此 tree-shaking 也可以用于类声明,而不再仅仅是函数。 -我们只需用 Babili 替换 UglifyJS,然后删除 babel-loader 即可。另一种方式是将 Babili 作为 Babel 的预设,仅使用 babel-loader(移除 UglifyJS 插件)。推荐使用第一种(插件的方式),因为当编译器不是 Babel(比如 Typescript)时,也能生效。 +我们只需用 Babili 替换 UglifyJS,然后删除 babel-loader 即可。另一种方式是将 Babili 作为 Babel 的预设,仅使用 babel-loader(移除 UglifyJS 插件)。推荐使用第一种(插件的方式),因为当编译器不是 Babel(比如 Typescript)时,它也能生效。 ``` module: { @@ -217,7 +217,7 @@ plugins: [ 我们需要将 ES6+ 代码传给 BabiliPlugin,否则它不用移除(未使用的)类。 -使用 Typescript 等编译器时,也应当使用 ES6+。Typescript应当输出 ES6+ 代码,以便 tree-shaking 能够生效。 +使用 Typescript 等编译器时,也应当使用 ES6+。Typescript 应当输出 ES6+ 代码,以便 tree-shaking 能够生效。 现在的输出不再包含类 *V6Engine*:[压缩后代码](https://github.com/blacksonic/babel-webpack-tree-shaking/blob/master/dist/car.es2015.prod.bundle.js)。 @@ -225,7 +225,7 @@ plugins: [ 对第三方包来说也是,应当使用 ES6 模块。幸运的是,越来越多的包作者同时发布 CommonJS 格式 和 ES6 格式的模块。ES6 模块的入口由 *package.json* 的字段 *module* 指定。 -对 ES6 模块,未使用的函数会被移除,但 class 并不一定会。只有当包内的 calss 定义也为 ES6 格式时,Babili 才能移除。很少有包能够以这种格式发布,但有的做到了(比如说 lodash 的 lodash-es)。 +对 ES6 模块,未使用的函数会被移除,但 class 并不一定会。只有当包内的 class 定义也为 ES6 格式时,Babili 才能将其移除。很少有包能够以这种格式发布,但有的做到了(比如说 lodash 的 lodash-es)。 罪魁祸首是当包的单独文件通过扩展它们来修改其他模块时,导入文件有副作用。[RxJs](https://github.com/Reactive-Extensions/RxJS)就是一个例子。通过导入一个运算符来修改其中一个类,这些被认为是副作用,它们阻止代码进行 tree-shaking。 @@ -233,7 +233,7 @@ plugins: [ 通过 tree-shaking 你可以相当程度上减少应用的体积。Webpack 2 内置支持它,但其机制并不同于 Rollup。它会包含所有的代码,标记未使用的函数和函数,以便压缩工具能够移除。这就是对所有代码都进行 tree-shake 的困难之处。使用默认的压缩工具 UglifyJS,它仅移除未使用的函数和变量;Babili 支持 ES6,可以用它来移除类。我们还必须特别注意第三方模块发布的方式是否支持 tree-shaking。 -希望这篇文章澄清了 Webpack 的 tree-shaking 背后的原理,并为您提供了克服困难的思路。 +希望这篇文章阐述了 Webpack 的 tree-shaking 背后的原理,并为您提供了克服困难的思路。 实际例子请访问 [Babel](https://github.com/blacksonic/babel-webpack-tree-shaking)、[Typescript](https://github.com/blacksonic/typescript-webpack-tree-shaking)。 From ea776858ea753da529d0db8b03973c6ff8c7f119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=9B=E5=AE=9A=E8=B0=94=E7=9A=84=E7=8C=AB?= Date: Sat, 19 Aug 2017 10:18:31 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=A0=A1=E5=AF=B9?= =?UTF-8?q?=E8=80=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/how-to-do-proper-tree-shaking-in-webpack-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md b/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md index 4891bb8fc57..ec5cf7d84ee 100644 --- a/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md +++ b/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md @@ -4,7 +4,7 @@ > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md](https://github.com/xitu/gold-miner/blob/master/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md) > * 译者:[薛定谔的猫](https://github.com/Aladdin-ADD/) - > * 校对者: + > * 校对者:[lsvih](https://github.com/lsvih)、[lampui](https://github.com/lampui) # 如何在 Webpack 2 中使用 tree-shaking From 00019ff33f36b917d869dc3c77c99366f25e3e19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=9B=E5=AE=9A=E8=B0=94=E7=9A=84=E7=8C=AB?= Date: Mon, 21 Aug 2017 17:07:17 +0800 Subject: [PATCH 4/6] Update how-to-do-proper-tree-shaking-in-webpack-2.md --- TODO/how-to-do-proper-tree-shaking-in-webpack-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md b/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md index ec5cf7d84ee..5c58a0d37ea 100644 --- a/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md +++ b/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md @@ -239,7 +239,7 @@ plugins: [ --- -*Thanks for reading! If you liked this story, please recommend it by clicking the ❤ button on the side and sharing it on social media. Follow me on *[*Medium*](https://medium.com/@blacksonic86)* or *[*Twitter*](https://twitter.com/blacksonic86)* to read more about Javascript!* +*感谢阅读!请点击[原文](https://blog.craftlab.hu/how-to-do-proper-tree-shaking-in-webpack-2-e27852af8b21)中的 ❤,然后分享到社交媒体上。欢迎关注 *[*Medium*](https://medium.com/@blacksonic86)*,*[*Twitter*](https://twitter.com/blacksonic86)* 阅读更多有关 Javascript 的内容!* --- From f7ab957ba50d2b56d278a398e7284859092ecc17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=9B=E5=AE=9A=E8=B0=94=E7=9A=84=E7=8C=AB?= Date: Mon, 21 Aug 2017 17:09:57 +0800 Subject: [PATCH 5/6] Update how-to-do-proper-tree-shaking-in-webpack-2.md --- TODO/how-to-do-proper-tree-shaking-in-webpack-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md b/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md index 5c58a0d37ea..ecbfa05efd4 100644 --- a/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md +++ b/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md @@ -239,7 +239,7 @@ plugins: [ --- -*感谢阅读!请点击[原文](https://blog.craftlab.hu/how-to-do-proper-tree-shaking-in-webpack-2-e27852af8b21)中的 ❤,然后分享到社交媒体上。欢迎关注 *[*Medium*](https://medium.com/@blacksonic86)*,*[*Twitter*](https://twitter.com/blacksonic86)* 阅读更多有关 Javascript 的内容!* +*感谢阅读!请点击[原文](https://blog.craftlab.hu/how-to-do-proper-tree-shaking-in-webpack-2-e27852af8b21)中的 ❤,然后分享到社交媒体上。欢迎关注 [*Medium*](https://medium.com/@blacksonic86),[*Twitter*](https://twitter.com/blacksonic86) 阅读更多有关 Javascript 的内容!* --- From b462f0f26957e1a77996bf00dcdb82a26182bebe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=9B=E5=AE=9A=E8=B0=94=E7=9A=84=E7=8C=AB?= Date: Mon, 21 Aug 2017 17:13:29 +0800 Subject: [PATCH 6/6] Update how-to-do-proper-tree-shaking-in-webpack-2.md --- TODO/how-to-do-proper-tree-shaking-in-webpack-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md b/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md index ecbfa05efd4..ab338188d3c 100644 --- a/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md +++ b/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md @@ -239,7 +239,7 @@ plugins: [ --- -*感谢阅读!请点击[原文](https://blog.craftlab.hu/how-to-do-proper-tree-shaking-in-webpack-2-e27852af8b21)中的 ❤,然后分享到社交媒体上。欢迎关注 [*Medium*](https://medium.com/@blacksonic86),[*Twitter*](https://twitter.com/blacksonic86) 阅读更多有关 Javascript 的内容!* +*感谢阅读!喜欢本文请点击[原文](https://blog.craftlab.hu/how-to-do-proper-tree-shaking-in-webpack-2-e27852af8b21)中的 ❤,然后分享到社交媒体上。欢迎关注 [*Medium*](https://medium.com/@blacksonic86),[*Twitter*](https://twitter.com/blacksonic86) 阅读更多有关 Javascript 的内容!* ---