摇树优化,即打包编译的时候移除未使用的代码。但是有一个前提,我们编写的代码必须是基于 ES6 Module 的静态结构特性的。
以下是静态结构的代码。
import a from './a';
import b from './b';
export default a;
以下是非静态结构的代码。
console.log('index.js');
const flag = Math.random();
if (flag) {
module.exports = 1;
} else {
module.exports = {};
}
if (flag) {
require('./a.js');
} else {
require('./b.js');
}
准备 demo
unused.js
const a = 1;
const b = () => {
return 'test.js';
}
export {a, b};
math.js
export function add(a, b) {
return a + b;
}
export function sub(a, b) {
return a - b;
}
inde.js
// 它包含了两个函数,只使用其中的一个函数
import {add} from './math';
// 只引入但未使用
import * as unused from './unused';
const result = add(55, 45);
console.log(result);
开发环境测试
webpack.config.js
var HtmlWebpackPlugin = require('html-webpack-plugin');
var {
CleanWebpackPlugin
} = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
index: './index.js',
},
output: {
publicPath: '',
path: __dirname + '/dist',
filename: '[name].js'
},
module: {
rules: []
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './index.html'
}),
]
}
./dist/index.js (代码片段)
"./index.js":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval(`__webpack_require__.r(__webpack_exports__);\n
/* harmony import */
var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./math */ \"./math.js\");\n
/* harmony import */
var _unused__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./unused */ \"./unused.js\");\n\r\n\r\n\r\n
const result = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"add\"])(55, 45);\r\n
console.log(result);\n\n
//# sourceURL=webpack:///./index.js?`);
}),
"./math.js":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval(`__webpack_require__.r(__webpack_exports__);\n
/* harmony export (binding) */
__webpack_require__.d(__webpack_exports__, \"add\", function() { return add; });\n
/* harmony export (binding) */
__webpack_require__.d(__webpack_exports__, \"sub\", function() { return sub; });\n
function add(a, b) {\r\n return a + b;\r\n}\r\n
function sub(a, b) {\r\n return a - b;\r\n}\n\n
//# sourceURL=webpack:///./math.js?`);
}),
"./unused.js":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval(`__webpack_require__.r(__webpack_exports__);\n
/* harmony export (binding) */
__webpack_require__.d(__webpack_exports__, \"a\", function() { return a; });\n
/* harmony export (binding) */
__webpack_require__.d(__webpack_exports__, \"b\", function() { return b; });\n
const a = 1;\r\n
const b = () => {\r\n
return 'test.js';\r\n
}\r\n\n\n
//# sourceURL=webpack:///./unused.js?`);
})
发现默认情况下,webpack不会进行摇树优化。
在 webpack 的配置文件中加入 optimization.usedExports 的配置(可以对代码做一些注释标记,方便其他优化工具或代码使用,如标记未使用的代码)。
webpack.cofig.js
var HtmlWebpackPlugin = require('html-webpack-plugin');
var {
CleanWebpackPlugin
} = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
index: './index.js',
},
optimization: {
usedExports: true
},
output: {
publicPath: '',
path: __dirname + '/dist',
filename: '[name].js'
},
module: {
rules: []
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './index.html'
}),
]
}
./dis/index.js (代码片段)
"./index.js":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval(`__webpack_require__.r(__webpack_exports__);\n
/* harmony import */
var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./math */ \"./math.js\");\n
/* harmony import */
var _unused__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./unused */ \"./unused.js\");\n\r\n\r\n\r\n
const result = Object(_math__WEBPACK_IMPORTED_MODULE_0__[/* add */ \"a\"])(55, 45);\r\n
console.log(result);\n\n
//# sourceURL=webpack:///./index.js?`);
}),
"./math.js":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval(`/* harmony export (binding) */
__webpack_require__.d(__webpack_exports__, \"a\", function() { return add; });\n
/* unused harmony export sub */\n
function add(a, b) {\r\n return a + b;\r\n}\r\n
function sub(a, b) {\r\n return a - b;\r\n}\n\n
//# sourceURL=webpack:///./math.js?`);
}),
"./unused.js":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval(`
/* unused harmony export a */\n
/* unused harmony export b */\n
const a = 1;\r\n
const b = () => {\r\n return 'test.js';\r\n}\r\n\n\n
//# sourceURL=webpack:///./unused.js?`);
})
在开发环境下,如果配置了 optimization.usedExports 为 true ,那么会标记未使用的代码,但并不会真正的去删除它们,如果要删除这些未使用的代码,需要配置为生产环境。
生产环境测试
webpack.config.js
var HtmlWebpackPlugin = require('html-webpack-plugin');
var {
CleanWebpackPlugin
} = require('clean-webpack-plugin');
module.exports = {
mode: 'production',
entry: {
index: './index.js',
},
output: {
publicPath: '',
path: __dirname + '/dist',
filename: '[name].js'
},
module: {
rules: []
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './index.html'
}),
]
}
./dis/index.js
! function (e) {
var t = {};
function n(r) {
if (t[r]) return t[r].exports;
var o = t[r] = {
i: r,
l: !1,
exports: {}
};
return e[r].call(o.exports, o, o.exports, n), o.l = !0, o.exports
}
n.m = e, n.c = t, n.d = function (e, t, r) {
n.o(e, t) || Object.defineProperty(e, t, {
enumerable: !0,
get: r
})
}, n.r = function (e) {
"undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, {
value: "Module"
}), Object.defineProperty(e, "__esModule", {
value: !0
})
}, n.t = function (e, t) {
if (1 & t && (e = n(e)), 8 & t) return e;
if (4 & t && "object" == typeof e && e && e.__esModule) return e;
var r = Object.create(null);
if (n.r(r), Object.defineProperty(r, "default", {
enumerable: !0,
value: e
}), 2 & t && "string" != typeof e)
for (var o in e) n.d(r, o, function (t) {
return e[t]
}.bind(null, o));
return r
}, n.n = function (e) {
var t = e && e.__esModule ? function () {
return e.default
} : function () {
return e
};
return n.d(t, "a", t), t
}, n.o = function (e, t) {
return Object.prototype.hasOwnProperty.call(e, t)
}, n.p = "", n(n.s = 0)
}([function (e, t, n) {
"use strict";
n.r(t);
const r = function (e, t) {
return e + t
}(55, 45);
console.log(r)
}]);
在生产模式下,webpack 对代码已经做了极致优化,发现未使用的代码都不见了。。
sideEffects
sideEffects 指的是有副作用的代码,假如模块 A 中包含一些影响全局作用域(非模块作用域)的代码,如改变了全局的变量或对象的变量(例如一些 polyfill ),模块 B 引入了模块 A ,但只使用它的部分导出或根本没有使用它的导出。在这种情况下,模块 A 就被认为是有副作用的,webpack 是不会删除模块 A 中所有未使用的代码的,它还会保留模块 A 中立即执行并对全局环境有影响的代码!
math.js
export function add(a, b) {
return a + b;
}
export function sub(a, b) {
return a - b;
}
// 副作用的代码,修改了全局的东西,会被保留
Number.prototype.add = add;
如果项目中所有模块都不会有副作用,那么可以在 webpack 的配置中将 optimization.sideEffects 设为 false ,这样可以提高删除未使用代码的编译速度。如果有某些模块是有副作用的,那么可以将它的路径加入 package.json 的 sideEffects 数组选项中,这样也能提高删除未使用代码的速度。
package.json
{
"name": "webpack",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"dev": "webpack"
},
"sideEffects": [
"./math.js"
]
}
当然,sideEffects 这个选项不是必须要配置的!