网页
综合
更新时间 2026-11-14 16:20:56
Tree Shaking | webpack
自动蜘蛛
webpack.js.org
正文约 6000 字
Tree shaking is a term commonly used in the JavaScript context for dead-code elimination. It relies on the static structure of ES2015 module syntax, i.e. import and export. The name and concept have been popularized by the ES2015 module bundler rollup.
The webpack 2 release came with built-in support for ES2015 modules (alias harmony modules) as well as unused module export detection. The new webpack 4 release expands on this capability with a way to provide hints to the compiler via the "sideEffects" package.json property to denote which files in your project are "pure" and therefore safe to prune if unused.
The remainder of this guide will stem from Getting Started. If you haven't read through that guide already, please do so now.
Let's add a new utility file to our project, src/math.js, that exports two functions:
Set the mode configuration option to development to make sure that the bundle is not minified:
With that in place, let's update our entry script to utilize one of these new methods and remove lodash for simplicity:
Note that we did not import the square method from the src/math.js module. That function is what's known as "dead code", meaning an unused export that should be dropped. Now let's run our npm script, npm run build, and inspect the output bundle:
dist/bundle.js (around lines 5 - 25)
Note the unused harmony export square comment above. If you look at the code below it, you'll notice that square is not being imported, however, it is still included in the bundle. We'll fix that in the next section.
In a 100% ESM module world, identifying side effects is straightforward. However, we aren't there quite yet, so in the mean time it's necessary to provide hints to webpack's compiler on the "pureness" of your code.
The way this is accomplished is the "sideEffects" package.json property.
All the code noted above does not contain side effects, so we can mark the property as false to inform webpack that it can safely prune unused exports.
A "side effect" is defined as code that performs a special behavior when imported, other than exposing one or more exports. An example of this are polyfills, which affect the global scope and usually do not provide an export.
If your code did have some side effects though, an array can be provided instead:
The array accepts simple glob patterns to the relevant files. It uses glob-to-regexp under the hood (Supports: *, **, {a,b}, [a-z]). Patterns like *.css, which do not include a /, will be treated like **/*.css.
Note that any imported file is subject to tree shaking. A CSS file imported for its side effect alone needs to be added to the side effect list so it will not be unintentionally dropped in production mode:
Finally, "sideEffects" can also be set from the module.rules configuration option.
The sideEffects and usedExports (more known as tree shaking) optimizations are two different things.
sideEffects is much more effective since it allows to skip whole modules/files and the complete subtree.
usedExports relies on terser to detect side effects in statements. It is a difficult task in JavaScript and not as effective as straightforward sideEffects flag. It also can't skip subtree/dependencies since the spec says that side effects need to be evaluated. While exporting function works fine, React's Higher Order Components (HOC) are problematic in this regard.
If you're using dynamic import(), you can also use the webpackExports magic comment to specify the exports that should be exposed, allowing webpack to tree shake the others. See Magic Comments.
Let's make an example:
The pre-bundled version looks like this:
When Button is unused you can effectively remove the export { Button$1 }; which leaves all the remaining code. So the question is "Does this code have any side effects or can it be safely removed?". Difficult to say, especially because of this line withAppProvider()(Button). withAppProvider is called and the return value is also called. Are there any side effects when calling merge or hoistStatics? Are there side effects when assigning WithProvider.contextTypes (Setter?) or when reading WrappedComponent.contextTypes (Getter?).
Terser actually tries to figure it out, but it doesn't know for sure in many cases. This doesn't mean that terser is not doing its job well because it can't figure it out. It's too difficult to determine it reliably in a dynamic language like JavaScript.
But we can help terser by using the /*#__PURE__*/ annotation. It flags a statement as side effect free. So a small change would make it possible to tree-shake the code:
This would allow to remove this piece of code. But there are still questions with the imports which need to be included/evaluated because they could contain side effects.
To tackle this, we use the "sideEffects" property in package.json.
It's similar to /*#__PURE__*/ but on a module level instead of a statement level. It says ("sideEffects" property): "If no direct export from a module flagged with no-sideEffects is used, the bundler can skip evaluating the module for side effects.".
In the Shopify's Polaris example, original modules look like this:
For import { Button } from "@shopify/polaris"; this has the following implications:
Specifically per matching resource(s):
In this case only 4 modules are included into the bundle:
After this optimization, other optimizations can still apply. For example: buttonFrom and buttonsFrom exports from Button.js are unused too. usedExports optimization will pick it up and terser may be able to drop some statements from the module.
Module Concatenation also applies. So that these 4 modules plus the entry module (and probably more dependencies) can be concatenated. index.js has no code generated in the end.
To better understand the impact of the sideEffects flag, let's look at a complete example of an npm package with CSS assets and how they might be affected during tree shaking. We'll create a fictional UI component library called "awesome-ui".
Our example pa
本页为82搜索生成的网页快照,内容来自站内索引;演示数据的原文链接仅作示例。