tree-shaking
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTree Shaking
Tree Shaking
It can happen that we add code to our bundle that isn't used anywhere in our application. This piece of dead code can be eliminated in order to reduce the size of the bundle, and prevent unnecessarily loading more data! The process of eliminating dead code before adding it to our bundle, is called tree-shaking.
我们可能会向包中添加一些在应用中从未使用过的代码。这些死代码可以被移除,从而减小包体积,避免不必要的数据加载!在将代码加入包之前移除死代码的过程被称为Tree Shaking。
When to Use
使用场景
- Use this when your bundle includes unused code from imported modules
- This is helpful for keeping JavaScript bundles lean and improving load performance
- 当你的包中包含来自导入模块的未使用代码时使用此方法
- 这有助于保持JavaScript包精简,提升加载性能
Instructions
操作指南
- Use ES2015 /
importsyntax — only ES modules can be tree-shakenexport - Use named imports instead of importing entire modules to enable effective tree-shaking
- Mark packages as side-effect-free in when appropriate
package.json - Be aware that modules with side effects cannot be safely tree-shaken
- 使用ES2015的/
import语法——只有ES模块支持Tree Shakingexport - 使用命名导入而非导入整个模块,以实现有效的Tree Shaking
- 适当时在中将标记包为无副作用
package.json - 注意:带有副作用的模块无法安全地进行Tree Shaking
Details
详细说明
Concepts
核心概念
Tree shaking is aimed at removing code that will never be used from a final JavaScript bundle. When done right, it can reduce the size of your JavaScript bundles and lower download, parse and (in some cases) execution time. For most modern JavaScript apps that use a module bundler (like webpack or Rollup), your bundler is what you would expect to automatically remove dead code.
Consider your application and its dependencies as an abstract syntax tree (we want to "shake" the syntax tree to optimize it). Each node in the tree is a dependency that gives your app functionality. In tree shaking, input files are treated as a graph. Each node in the graph is a top level statement which is called a "part" in the code. Tree shaking is a graph traversal which starts from the entry point and marks any traversed paths for inclusion.
Every component can declare symbols, reference symbols, and rely on other files. Even the "parts" are marked as having side effects or not. For example, the statement has no side effects because the statement can be removed without any observed difference if nothing needs firstName. But the statement has side effects, because the call to can not be removed without changing the meaning of the code, even if nothing needs firstName.
let firstName = 'Jane'let firstName = getName()getName()Tree Shaking旨在从最终的JavaScript包中移除永远不会被使用的代码。操作得当的话,它可以减小JavaScript包的体积,缩短下载、解析(在某些情况下)以及执行时间。对于大多数使用模块打包工具(如webpack或Rollup)的现代JavaScript应用,打包工具会自动移除死代码。
可以将应用及其依赖视为一棵抽象语法树(我们需要“摇晃”这棵语法树来进行优化)。树中的每个节点都是为应用提供功能的依赖项。在Tree Shaking中,输入文件被视为一个图。图中的每个节点都是一个顶级语句,在代码中被称为一个“部分”。Tree Shaking是一种从入口点开始的图遍历过程,会标记所有遍历到的路径以纳入最终包中。
每个组件都可以声明符号、引用符号,并依赖其他文件。甚至这些“部分”也会被标记为是否有副作用。例如,语句没有副作用,因为如果没有任何地方需要firstName,移除该语句不会产生任何可见差异。但语句有副作用,因为即使没有地方需要firstName,移除对的调用也会改变代码的含义。
let firstName = 'Jane'let firstName = getName()getName()Imports
导入方式
Only modules defined with the ES2015 module syntax ( and ) can be tree-shaken. The way you import modules specifies whether the module can be tree-shaken or not.
importexportTree shaking starts by visiting all parts of the entry point file with side effects, and proceeds to traverse the edges of the graph until new sections are reached. Once the traversal is completed, the JavaScript bundle includes only the parts that were reached during the traversal. The other pieces are left out.
Let's say we define the following file:
utilities.jsjs
export function read(props) {
return props.book
}
export function nap(props) {
return props.winks
}Then we have the following index.js file:
js
import { read } from 'utilities';
eventHandler = (e) => {
read({ book: e.target.value })
}In this example, isn't important and therefore won't be included in the bundle.
nap()只有使用ES2015模块语法(和)定义的模块才能进行Tree Shaking。导入模块的方式决定了该模块是否可以被Tree Shaking处理。
importexportTree Shaking从入口文件中所有带有副作用的部分开始遍历,然后沿着图的边继续遍历,直到到达新的部分。遍历完成后,JavaScript包只会包含遍历过程中到达的部分,其他部分则会被排除在外。
假设我们定义了如下的文件:
utilities.jsjs
export function read(props) {
return props.book
}
export function nap(props) {
return props.winks
}然后我们有如下的index.js文件:
js
import { read } from 'utilities';
eventHandler = (e) => {
read({ book: e.target.value })
}在这个例子中,并未被使用,因此不会被包含在最终包中。
nap()Side Effects
副作用
When we're importing an ES6 module, this module gets executed instantly. It could happen that although we're not referencing the module's exports anywhere in our code, the module itself affects the global scope while it's being executed (polyfills or global stylesheets, for example). This is called a side effect. Although we're not referencing the exports of the module itself, if the module has exported values to begin with, the module cannot be tree-shaken due to the special behavior when it's being imported!
The Webpack documentation gives a clear explanation on tree-shaking and how to avoid breaking it.
当我们导入一个ES6模块时,该模块会立即执行。可能会出现这种情况:尽管我们的代码中从未引用该模块的导出内容,但模块本身在执行时会影响全局作用域(例如polyfills或全局样式表)。这被称为副作用。即使我们没有引用模块的导出内容,如果该模块本身有导出值,由于导入时的特殊行为,该模块也无法被Tree Shaking处理!
Webpack文档对Tree Shaking以及如何避免破坏它有清晰的解释。