webpack 2中的Tree Shaking

Tree Shaking?是一个术语,字面的理解就是摇一摇树,树上的枯叶就会掉下来,留下绿叶。

普及了 JavaScript 圈内一个重要的特性:Tree shaking,即是指消除JavaScript上下文中无用代码,或更精确地说,只保留有用的代码。它依赖于ES6模块 / ?模块系统的来检测哪一个模块没有被使用,因为,import 和 export 不会在运行时改变。说的再直白一点就是Tree shaking 从模块包中排除未使用的 exports 项。

webpack 2 内置引入的 Tree-shaking 代码优化技术。

示例

看一下官方文档提供的示例:

考虑一个 maths.js 库文件导出两个函数,squarecube

// 这个函数不在任何地方被使用
export function square(x) {
    return x * x; //平方
}

// 这个函数被其他脚本使用
export function cube(x) {
    return x * x * x; //立方
}

在我们的 main.js 中,我们选择性地导入 cube

import {cube} from './maths.js';
console.log(cube(5)); // 125

运行 node_modules/.bin/webpack main.js dist.js 并检查 dist.js 显示没有导出square,请查看 “unused harmony export square”的注释,再查看/* harmony export (immutable) */ __webpack_exports__["a"] = cube;这条语句,显示这里只导出了cube

/* ... webpackBootstrap ... */
/******/ ([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* unused harmony export square */
/* harmony export (immutable) */ __webpack_exports__["a"] = cube;
// This function isn't used anywhere
function square(x) {
  return x * x;
}

// This function gets included
function cube(x) {
  return x * x * x;
}

/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__maths_js__ = __webpack_require__(0);

console.log(__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__maths_js__["a" /* cube */])(5)); // 125

/***/ })

当运行生产环境构建node_modules/.bin/webpack --optimize-minimize main.js dist.min.js时,只保留了 cube 的压缩版本,而 square 并没有保留在构建的 bundle 中:

/* ... */
function(e,t,n){"use strict";function r(e){return e*e*e}t.a=r}
/* ... */
function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0);console.log(n.i(r.a)(5))}

讨论

大家可以看看关于Tree Shaking 的讨论:

赞同尤雨溪的观点

指出的一点就是不管是 rollup 还是 webpack 2,tree-shaking 都是因为 ES6 modules 的静态特性才得以实现的。ES6 modules 的 import 和 export statements 相比完全动态的 CommonJS require,有着本质的区别。举例来说:

1. 只能作为模块顶层的语句出现,不能出现在 function 里面或是 if 里面。(ECMA-262 15.2)
2. import 的模块名只能是字符串常量。(ECMA-262 15.2.2)
3. 不管 import 的语句出现的位置在哪里,在模块初始化的时候所有的 import 都必须已经导入完成。换句话说,ES6 imports are hoisted。(ECMA-262 15.2.1.16.4 – 8.a)
4. import binding 是 immutable 的,类似 const。比如说你不能 import { a } from ‘./a’ 然后给 a 赋值个其他什么东西。(ECMA-262 15.2.1.16.4 – 12.c.3)

这些设计虽然使得灵活性不如 CommonJS 的 require,但却保证了 ES6 modules 的依赖关系是确定 (deterministic) 的,和运行时的状态无关,从而也就保证了 ES6 modules 是可以进行可靠的静态分析的。对于主要在服务端运行的 Node 来说,所有的代码都在本地,按需动态 require 即可,但对于要下发到客户端的 web 代码而言,要做到高效的按需使用,不能等到代码执行了才知道模块的依赖,必须要从模块的静态分析入手。这是 ES6 modules 在设计时的一个重要考量,也是为什么没有直接采用 CommonJS。

正是基于这个基础上,才使得 tree-shaking 成为可能(这也是为什么 rollup 和 webpack 2 都要用 ES6 module syntax 才能 tree-shaking),所以说与其说 tree-shaking 这个技术怎么了不起,不如说是 ES6 module 的设计在模块静态分析上的种种考量值得赞赏。

相关阅读链接

赞(0) 打赏
未经允许不得转载:WEBTian开发 » webpack 2中的Tree Shaking

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

Tian开发相关广告投放 更专业 更精准

联系我们

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏