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

如何在 webpack2 中使用 tree-shaking #2076

Merged
merged 6 commits into from
Aug 22, 2017
Merged

Conversation

aladdin-add
Copy link
Member

@aladdin-add aladdin-add commented Aug 17, 2017

fixes #2075

@aladdin-add aladdin-add force-pushed the patch-1 branch 2 times, most recently from 175717e to e4aefba Compare August 18, 2017 01:46
@linhe0x0 linhe0x0 changed the title WIP: [译] 如何在webpack2中使用tree-shaking 如何在 webpack2 中使用 tree-shaking Aug 18, 2017
@lsvih
Copy link
Member

lsvih commented Aug 18, 2017

校对认领

@linhe0x0
Copy link
Member

@lsvih 好的呢 🍺


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 中使用这一特性,如何克服使用中的难点。
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It has been made available by the static nature of ES2015 modules
它的可行性基于 ES6 的静态模块
->
它依赖于 ES6 静态模块


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 *类。让我们来看看它是如何工作的。
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这儿 V8Engine 和 SportsCar 的样式有些问题,可以在 github 自带的预览里看看 https://github.com/Aladdin-ADD/gold-miner/blob/c4d41d34c2b9ca7097b6e61c6934a31fcaca963b/TODO/how-to-do-proper-tree-shaking-in-webpack-2.md


![](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)等),可以得到如下输出:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

变换 -> 编译器 或者 变换工具

压缩 -> 压缩器 或者 压缩工具

@@ -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 没有生效吗?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

你自然会问 -> 你应该会问:

@@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这句话漏了

可以写作 移除死代码 vs 包容活代码

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

emmm,“包容” 好像有点奇怪,您再看看哈。。

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这句我就是觉得不好翻译,就放着了。。lol


*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的代码在初始化时有副作用。
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

转换为 ES5的代码
->
转换为 ES5 的代码


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 能够生效。
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typescript应当
->
Typescript 应当

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The output of Typescript will be handed over to Babili to remove the unused code.
这句话漏了

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

以便 tree-shaking 能够生效
->
以便使 tree-shaking 能够生效


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)。
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calss
->
class


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)。
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Babili 才能移除
->
Babili 才能将其移除


I hope this article clarifies the inner workings behind Webpack’s tree-shaking and gives you ideas to overcome the obstacles.
希望这篇文章澄清了 Webpacktree-shaking 背后的原理,并为您提供了克服困难的思路。
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

澄清 -> 阐明

@lsvih
Copy link
Member

lsvih commented Aug 18, 2017

校对完毕

@laampui
Copy link
Contributor

laampui commented Aug 18, 2017

校对认领

@fanyijihua
Copy link
Collaborator

@lampui 妥妥哒 🍻

Copy link
Contributor

@laampui laampui left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aladdin-add 校对完毕


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* 类。让我们来看看它是如何工作的。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

『我们期望输出包只包含用到的类和函数』,这句话有点怪


![](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)等),可以得到如下输出:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

『编译器(Babel等)』=> 中英文之间需要空格


![](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)等),可以得到如下输出:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

『压缩工具(UglifyJS等)』=>中英文之间需要空格

@@ -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 没有生效吗?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

『...用注释 /unused harmony export V6Engine/ 将未使用的...』=>
『...用注释 /* unused harmony export V6Engine */ 将未使用的...』

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

『用 /harmony export (immutable)/ webpack_exports[“a”] = V8Engine; 来标记用到的』=>
『用 /* harmony export (immutable) */ __webpack_exports__[“a”] = V8Engine; 来标记用到的』


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不同,它(的打包)只包含运行应用程序所必需的代码。打包完成后的输出并没有未使用的类和函数,压缩仅涉及实际使用的代码。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

『而Rollup不同』=>
『而 Rollup 不同』,
有研究显示,打字的时候不喜欢在中文和英文之间加空格的人,感情路都走得很辛苦。。。


![](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 语法。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

『最后,Webpack将把它们转换为 CommonJS 语法。』=>
『最后,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 预设(在这个例子中是[babel-preset-env](https://github.com/babel/babel-preset-env))不要转换 module
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

『...在这个例子中是[babel-preset-env](https://』=>
『...在这个例子中是 [babel-preset-env](https://』
有研究显示,......空格......


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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

『...还需编译器支持将类编译后的赋值标记为 @PURE。』=>
『...还需编译器支持将类编译后的赋值标记为 @__PURE__。』


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 一样。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

『何不开发一个基于 Babel 的代码压缩工具,就能够识别 ES6+ 的语法了。』=>
『为什么不开发一个基于 Babel 的代码压缩工具,就能够识别 ES6+ 的语法呢?』


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 一样。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

『所以他们开发了Babili』=>
『所以他们开发了 Babili』,空格

@aladdin-add
Copy link
Member Author

@sqrthree 已经根据校对者意见修改。 🚀 🚀 🚀


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)

---

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这句英文也翻译下吧。

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已经翻译了啊~

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get~

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!

@linhe0x0 linhe0x0 merged commit b78ea0e into xitu:master Aug 22, 2017
@linhe0x0
Copy link
Member

@aladdin-add 已经 merge 啦~ 快快麻溜发布到掘金专栏然后给我发下链接,方便及时添加积分哟。

@aladdin-add
Copy link
Member Author

atuooo added a commit to atuooo/gold-miner that referenced this pull request Aug 23, 2017
…slate

* 'master' of https://github.com/xitu/gold-miner: (178 commits)
  🚀 添加文章『关于 React Router 4 的一切』到文章列表
  🚀 添加文章『为什么我们渴求女性来设计 AI 』到文章列表
  🚀 添加文章『如何在 Webpack 2 中使用 tree-shaking』到文章列表
  🚀 添加文章『Redux 有多棒?』到文章列表
  🚀 添加文章『别再使用图片轮播了』到文章列表
  🚀 添加文章『将 Android 项目迁移到 Kotlin 语言』到文章列表
  🚀 添加文章『巧用 ARKit 和 SpriteKit 从零开始做 AR 游戏』到文章列表
  别再使用图片轮播了 (xitu#2073)
  Update how-to-do-proper-tree-shaking-in-webpack-2.md (xitu#2100)
  为什么我们渴求女性来设计 AI (xitu#2074)
  如何在 webpack2 中使用 tree-shaking (xitu#2076)
  怎么写出完美的错误消息 (xitu#2080)
  巧用 ARKit 和 SpriteKit 从零开始做 AR 游戏 (xitu#2043)
  将 Android 程序移植为 Kotlin 程序 (xitu#2039)
  Redux 有多棒? (xitu#2003)
  🚀 添加文章『函数式响应编程入门指南』到文章列表
  🚀 添加文章『虚拟现实是如何改变用户体验的:从原型到设备的设计』到文章列表
  ✨ Create angular-vs-react-which-is-better-for-web-development.md
  ✨ Create evolving-the-facebook-news-feed-to-serve-you-better.md
  ✨ Create why-context-value-matters-and-how-to-improve-it.md
  ...
cdadar pushed a commit to cdadar/gold-miner that referenced this pull request Dec 8, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

如何在 webpack2 中使用 tree-shaking
5 participants