Tree Shaking is used to clean up exported module code that conforms to the ESModule specification and is not referenced in the application context. In Webpack, the Tree Shaking operation is done primarily through the following configuration:
optimization.providedExports
optimization.usedExports
optimization.innerGraph
optimization.sideEffects
Next, let’s illustrate the above configuration with a concrete example:
// src/net.js
export const HOST = 'localhost';
export const PORT = 3000;
// src/config.js
export * from './net';
// src/utils.js
import { HOST } from './config';
function getHost() {
return HOST;
}
export function echoHello() {
console.log('hello');
}
export function echoHost() {
console.log(getHost());
}
// src/index.js
import { echoHello } from './utils';
echoHello();
Copy the code
In the code snippet above, we do the following things:
- in
src/net.js
exportHOST
,PORT
Variables; - in
src/config.js
Through theexport * from
Derived statementssrc/net.js
All exported members in; - in
src/utils.js
The introduction ofsrc/config.js
Derived member ofHOST
, and then definegetHost
,echoHello
及echoHost
Function, and then exportechoHello
和echoHost
Functions; - in
src/index.js
The introduction ofsrc/utils.js
Derived function ofechoHello
, and then call the function.
optimization.providedExports
The default value for this option is true; Its main purpose is to generate more efficient code for the export * FROM statement by collecting the export members of the related module and exporting them concretely, as in the following example:
// webpack.config.js
module.exports = {
mode: 'development'.devtool: false.optimization: {
providedExports: false,}};Copy the code
SRC /config.js: SRC /config.js
/ * * * / "./src/config.js":
/ *! * * * * * * * * * * * * * * * * * * * * * * *! * \! *** ./src/config.js ***! \ * * * * * * * * * * * * * * * * * * * * * * * /
/ * * * / ((__unused_webpack_module, __webpack_exports__, __webpack_require__) = > {
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _net__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/ *! ./net */ "./src/net.js");
/* harmony reexport (unknown) */ var __WEBPACK_REEXPORT_OBJECT__ = {};
/* harmony reexport (unknown) */ for(const __WEBPACK_IMPORT_KEY__ in _net__WEBPACK_IMPORTED_MODULE_0__) if(__WEBPACK_IMPORT_KEY__ ! = ="default") __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = () = > _net__WEBPACK_IMPORTED_MODULE_0__[__WEBPACK_IMPORT_KEY__]
/* harmony reexport (unknown) */ __webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__);
/ * * * / }),
Copy the code
Will optimization. ProvidedExports value change is true, observe again SRC/config. Js the effect after the packaging:
/ * * * / "./src/config.js":
/ *! * * * * * * * * * * * * * * * * * * * * * * *! * \! *** ./src/config.js ***! \ * * * * * * * * * * * * * * * * * * * * * * * /
/ * * * / ((__unused_webpack_module, __webpack_exports__, __webpack_require__) = > {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "HOST": () = > (/* reexport safe */ _net__WEBPACK_IMPORTED_MODULE_0__.HOST),
/* harmony export */ "PORT": () = > (/* reexport safe */ _net__WEBPACK_IMPORTED_MODULE_0__.PORT)
/* harmony export */ });
/* harmony import */ var _net__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/ *! ./net */ "./src/net.js");
/ * * * / }),
Copy the code
Contrast after packaging code, open optimization. After providedExports option, the generated code to clear need for traverse _net__WEBPACK_IMPORTED_MODULE_0__ related modules to be exported by the concrete members, the code is more short and efficient. Note, however, that even if this option is set to false, it does not affect the Tree Shaking for Webpack.
optimization.usedExports
The default value for this option is true in production and false in other environments; Its main function is to collect and annotate module export members that are not referenced by the program context, as in the following example:
// webpack.config.js
module.exports = {
mode: 'development'.devtool: false.optimization: {
providedExports: true.usedExports: false,}};Copy the code
SRC /net.js, SRC /config.js, SRC /utils.js, SRC /utils.
/ * * * / "./src/net.js":
/ *! * * * * * * * * * * * * * * * * * * * *! * \! *** ./src/net.js ***! \ * * * * * * * * * * * * * * * * * * * * /
/ * * * / ((__unused_webpack_module, __webpack_exports__, __webpack_require__) = > {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "HOST": () = > (/* binding */ HOST),
/* harmony export */ "PORT": () = > (/* binding */ PORT)
/* harmony export */ });
const HOST = 'localhost';
const PORT = 3000;
/ * * * / }),
/ * * * / "./src/config.js":
/ *! * * * * * * * * * * * * * * * * * * * * * * *! * \! *** ./src/config.js ***! \ * * * * * * * * * * * * * * * * * * * * * * * /
/ * * * / ((__unused_webpack_module, __webpack_exports__, __webpack_require__) = > {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "HOST": () = > (/* reexport safe */ _net__WEBPACK_IMPORTED_MODULE_0__.HOST),
/* harmony export */ "PORT": () = > (/* reexport safe */ _net__WEBPACK_IMPORTED_MODULE_0__.PORT)
/* harmony export */ });
/* harmony import */ var _net__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/ *! ./net */ "./src/net.js");
/ * * * / }),
/ * * * / "./src/config.js":
/ *! * * * * * * * * * * * * * * * * * * * * * * *! * \! *** ./src/config.js ***! \ * * * * * * * * * * * * * * * * * * * * * * * /
/ * * * / ((__unused_webpack_module, __webpack_exports__, __webpack_require__) = > {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "HOST": () = > (/* reexport safe */ _net__WEBPACK_IMPORTED_MODULE_0__.HOST),
/* harmony export */ "PORT": () = > (/* reexport safe */ _net__WEBPACK_IMPORTED_MODULE_0__.PORT)
/* harmony export */ });
/* harmony import */ var _net__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/ *! ./net */ "./src/net.js");
/ * * * / }),
Copy the code
Change the value of optimization.usedExports to true, and observe the effects of SRC /net.js, SRC /config.js, and SRC /utils.js:
/ * * * / "./src/net.js":
/ *! * * * * * * * * * * * * * * * * * * * *! * \! *** ./src/net.js ***! \ * * * * * * * * * * * * * * * * * * * * /
/ * * * / ((__unused_webpack_module, __webpack_exports__, __webpack_require__) = > {
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "HOST": () = > (/* binding */ HOST)
/* harmony export */ });
/* unused harmony export PORT */
const HOST = 'localhost';
const PORT = 3000;
/ * * * / }),
/ * * * / "./src/config.js":
/ *! * * * * * * * * * * * * * * * * * * * * * * *! * \! *** ./src/config.js ***! \ * * * * * * * * * * * * * * * * * * * * * * * /
/ * * * / ((__unused_webpack_module, __webpack_exports__, __webpack_require__) = > {
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "HOST": () = > (/* reexport safe */ _net__WEBPACK_IMPORTED_MODULE_0__.HOST)
/* harmony export */ });
/* harmony import */ var _net__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/ *! ./net */ "./src/net.js");
/ * * * / }),
/ * * * / "./src/utils.js":
/ *! * * * * * * * * * * * * * * * * * * * * * *! * \! *** ./src/utils.js ***! \ * * * * * * * * * * * * * * * * * * * * * * /
/ * * * / ((__unused_webpack_module, __webpack_exports__, __webpack_require__) = > {
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "echoHello": () = > (/* binding */ echoHello)
/* harmony export */ });
/* unused harmony export echoHost */
/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/ *! ./config */ "./src/config.js");
Copy the code
Comparing the packaged code, you can see that with the optimization.usedexports option on:
- in
__webpack_require__.d
Only export members that need to be used are defined in. - Export members that are not used are marked, for example
/* unused harmony export echoHost */
.
It is through the optimization.usedExports option that Webpack collects used and unused exported members and marks unused exported members for subsequent code optimization using plug-ins such as Terser-webpack-plugin. Remove these unused code snippets.
optimization.innerGraph
This option is a new feature introduced in Webpack 5 and defaults to true for production and false for other environments; Its main function is to build an internal dependency map, analyze flags in modules, and find dependencies between exports and references so that more useless code can be removed.
For example, the SRC /utils.js export function echoHost calls getHost, which in turn refers to the SRC /config.js export member HOST, but SRC /index.js echoHost is not referenced. In this case, Webpack can safely remove the code logic from SRC /config.js and SRC /net.js.
// webpack.config.js
module.exports = {
mode: 'development'.devtool: false.optimization: {
providedExports: true.usedExports: true.innerGraph: false,}};Copy the code
SRC /net.js SRC /config.js SRC /config.js SRC /net.js SRC /config.js SRC /config.js SRC /net.js SRC /config.js
/ * * * / "./src/net.js":
/ *! * * * * * * * * * * * * * * * * * * * *! * \! *** ./src/net.js ***! \ * * * * * * * * * * * * * * * * * * * * /
/ * * * / ((__unused_webpack_module, __webpack_exports__, __webpack_require__) = > {
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "HOST": () = > (/* binding */ HOST)
/* harmony export */ });
/* unused harmony export PORT */
const HOST = 'localhost';
const PORT = 3000;
/ * * * / }),
/ * * * / "./src/config.js":
/ *! * * * * * * * * * * * * * * * * * * * * * * *! * \! *** ./src/config.js ***! \ * * * * * * * * * * * * * * * * * * * * * * * /
/ * * * / ((__unused_webpack_module, __webpack_exports__, __webpack_require__) = > {
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "HOST": () = > (/* reexport safe */ _net__WEBPACK_IMPORTED_MODULE_0__.HOST)
/* harmony export */ });
/* harmony import */ var _net__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/ *! ./net */ "./src/net.js");
/ * * * / }),
Copy the code
Set optimization. InnerGraph to true and see SRC /net.js and SRC /config.js wrapped together:
/ * * * / "./src/net.js":
/ *! * * * * * * * * * * * * * * * * * * * *! * \! *** ./src/net.js ***! \ * * * * * * * * * * * * * * * * * * * * /
/ * * * / ((__unused_webpack_module, __webpack_exports__, __webpack_require__) = > {
/* unused harmony exports HOST, PORT */
const HOST = 'localhost';
const PORT = 3000;
/ * * * / }),
/ * * * / "./src/config.js":
/ *! * * * * * * * * * * * * * * * * * * * * * * *! * \! *** ./src/config.js ***! \ * * * * * * * * * * * * * * * * * * * * * * * /
/ * * * / ((__unused_webpack_module, __unused_webpack___webpack_exports__, __webpack_require__) = > {
/* harmony import */ var _net__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/ *! ./net */ "./src/net.js");
/ * * * / }),
Copy the code
Comparing the packaged code, you can see that with the optimization. InnerGraph option on:
src/net.js
,src/config.js
None of them__webpack_require__.d
Call;src/net.js
In theHOST
和PORT
All marked as unused.
With the above markup and processing, Webpack can clean up the code in SRC /net.js and SRC /config.js in subsequent steps.
Pure markup
Add the following code to SRC /utils.js:
console.log('dead code');
Copy the code
Then package in production mode:
(() = >{"use strict";console.log("dead code"),console.log("hello")}) ();Copy the code
If we want Webpack to remove debugging statements like console.log(“dead code”), we can do this with the pure tag:
/*#__PURE__*/
console.log('dead code');
Copy the code
Package again in production mode:
(() = >{"use strict";console.log("hello")}) ();Copy the code
At this point the console.log(“dead code”) statement is removed, and it is important to note that snippets tagged with pure cannot have side effects, otherwise unexpected exceptions will occur.
optimization.sideEffects
The default value for this option is true in production and flag in other environments. (When true, Webpack parses module code using JavaScriptParser to find code snippets with side effects. And log it into the ModuleGraph object); Its main role is to determine whether Webpack is safe to Tree shaking modules based on the sideEffects configuration in package.json; Its use involves two aspects:
Webpack
In theoptimization.sideEffects
Configuration to enable the side effect detection function;package.json
In thesideEffects
Configuration to tell Webpack NPM if the package has side effects of typebool
或string[]
(whenstring[]
, each item is a glob pattern matching string).
Take the following example:
// src/utils.js
Array.prototype.sum = function() {
return this.reduce((result, num) = > (result + num), 0);
}
// src/index.js
import './utils';
console.log([1.2.3].sum());
// webpack.config.js
module.exports = {
mode: 'production'.devtool: false.optimization: {
sideEffects: true,}};// package.json
{
"sideEffects": false,}Copy the code
In the code snippet above, we do the following things:
- in
src/utils.js
Through theArray
addedsum
Methods; - in
src/index.js
The introduction ofsrc/utils.js
And callArray
的sum
Methods; - in
webpack.config.js
Lt.optimization.sideEffects
Set totrue
; - in
package.json
Lt.sideEffects
Set tofalse
.
The result is as follows:
(() = >{"use strict";console.log([1.2.3].sum())})();
Copy the code
Since we set sideEffects to false in package.json, this tells Webpack to feel free to Tree shaking, but the above packaging code throws an exception for calling a method that doesn’t exist, which is called a side effect. To work, set the value in package.json to true or [“./ SRC /utils.js”] (string[] is recommended, so you can Tree shaking code that is not in the configuration list). The Tree shaking operation is not performed when you package the declared file:
(() = >{var r={555:() = >{Array.prototype.sum=function(){return this.reduce(((r,e) = >r+e),0)}}},e={};function t(o){var n=e[o];if(void 0! ==n)return n.exports;var u=e[o]={exports: {}};return r[o](u,u.exports,t),u.exports}t.n=r= >{var e=r&&r.__esModule?() = >r.default:() = >r;return t.d(e,{a:e}),e},t.d=(r,e) = >{for(var o ine)t.o(e,o)&&! t.o(r,o)&&Object.defineProperty(r,o,{enumerable:!0.get:e[o]})},t.o=(r,e) = >Object.prototype.hasOwnProperty.call(r,e),(() = >{"use strict"; t(555),console.log([1.2.3].sum())})()})();
Copy the code
As discussed, sideEffects is mainly used in scenarios that have global impact, such as manipulating window objects and loading CSS files.
conclusion
In this article we have taken a look at the use of Webpack Tree Shaking. Based on the configuration optimization. ProvidedExports, optimization. UsedExports, optimization. InnerGraph, optimization. The sideEffects and pure markup We believe that we can skillfully optimize the code through Webpack according to the business needs in the future work. Finally, I wish you all a happy coding every day. ^ _ ^