Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webpack3之Scope Hoisting #28

Open
wangning0 opened this issue Jul 11, 2017 · 6 comments
Open

webpack3之Scope Hoisting #28

wangning0 opened this issue Jul 11, 2017 · 6 comments

Comments

@wangning0
Copy link
Owner

wangning0 commented Jul 11, 2017

webpack3之Scope Hoisting

介绍

在webpack2发布半年不到,webpack3又发布了,这次的发布版本虽然跨度比较大,但是不同于webpack2的升级,它对于向下的兼容性还是要好很多的,但是由于内部的突破性变化导致某些插件的使用,根据官方的数据表明,目前为止,至少98%的用户都没有遇到升级过程中不兼容的问题。

在这次升级中,有以下特性:

  • 作用域的提升(Scope Hoisting)

  • 魔法注释(Magic Comments)

我们在这篇文章中,主要的对象是作用域的提升scope hoisting

如果对于webpack前两个版本了解的同学,应该知道,在打包后的文件里面,每个模块都会被包装在一个单独的函数闭包中,这些闭包会导致你JS在浏览器中的执行速度变慢,并且会导致内存占用率增加,而另外的打包工具rollup则是将所有的模块包装在一个大的闭包中

所以在webpack3中增加了这个功能,我们可以在配置项中添加对应的配置来开启该功能

module.exports = {
    // ...
    plugins: [
        new webpack.optimize.ModuleConcatenationPlugin()
    ]
}
    // ...

并且该功能也会让代码打包的体积变得更小,加快运行的速度

Scope Hoisting 和 Code splitting

我们都知道webpack有一个核心功能是Code-splitting, 并且在webpack2也开始支持了原生ES6的模块加载方案,那么对于按需加载的模块,肯定是在打包过程中,形成另外的打包文件的,那么webpack 是怎么做到 code splitting的呢?

所以在webpack中不能将所有所有的模块直接放在同一个作用域下,有以下几个原因:

  • 按需加载的模块
  • 使用commonjs规范的模块
  • 被不能在同一作用域的模块所共享的模块

可能说起来比较抽象,我们举个例子,来对比下webpack2和webpack3再打包同样的代码后生成的代码

webpack2 VS webpack3

我们都统一建立如下关系的文件,分别用webpack2和webpack3打包,观察下生成的打包文件的不同


71309eff-092d-4ecc-9486-3897d5d782f8

上图中实现表示的是同步的依赖关系,大箭头表示的是异步的依赖关系

根据我们上文中所说的规则,webpack会使用一种称为 “局部范围提升”的方法,该方法会选择可以展开的最大的ES模块进行变量提升,会将它们和webpack的默认原函数相连接

所以上述的模块之间的关系会变成下图:

db1d93a5-22e4-4263-9a08-5241fa6e7f86

index.js

import { a, x, y } from "./a";
import * as b from "./b";

import("./lazy").then(function(lazy) {
	console.log(a, b.a(), x, y, lazy.c, lazy.d.a, lazy.x, lazy.y);
});

lazy.js

export * from "./c";
import * as d from "./d";
export { d };

a.js

// module a
export var a = "a";
export * from "./shared";

b.js

// module b
export function a() {
	return "b";
};

c.js

// module c
import { c as e } from "./cjs";

export var c = String.fromCharCode(e.charCodeAt(0) - 2);

export { x, y } from "./shared";

d.js

// module d
export var a = "d";

cjs.js

// module cjs (commonjs)
exports.c = "e";

shared.js

// shared module
export var x = "x";
export * from "./shared2";

shared2.js

// shared2 module
export var y = "y";

webopack2打包的代码

只保留了模块的代码

// bundle.js
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return a; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__shared__ = __webpack_require__(2);
/* harmony namespace reexport (by used) */ __webpack_require__.d(__webpack_exports__, "b", function() { return __WEBPACK_IMPORTED_MODULE_0__shared__["a"]; });
/* harmony namespace reexport (by used) */ __webpack_require__.d(__webpack_exports__, "c", function() { return __WEBPACK_IMPORTED_MODULE_0__shared__["b"]; });
// module a
var a = "a";


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

"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = a;
// module b
function a() {
	return "b";
};

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

"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return x; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__shared2__ = __webpack_require__(5);
/* harmony namespace reexport (by used) */ __webpack_require__.d(__webpack_exports__, "b", function() { return __WEBPACK_IMPORTED_MODULE_0__shared2__["a"]; });
// shared module
var x = "x";


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

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



__webpack_require__.e/* import() */(0).then(__webpack_require__.bind(null, 4)).then(function(lazy) {
	console.log(__WEBPACK_IMPORTED_MODULE_0__a__["a"], __WEBPACK_IMPORTED_MODULE_1__b__["a"](), __WEBPACK_IMPORTED_MODULE_0__a__["b" /* x */], __WEBPACK_IMPORTED_MODULE_0__a__["c" /* y */], lazy.c, lazy.d.a, lazy.x, lazy.y);
});

/***/ }),
/* 4 */,
/* 5 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return y; });
// shared2 module
var y = "y";

/***/ })
/******/ ]);
// 0.bundle.js
webpackJsonp([0],[
/* 0 */,
/* 1 */,
/* 2 */,
/* 3 */,
/* 4 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__c__ = __webpack_require__(6);
/* harmony namespace reexport (by provided) */ __webpack_require__.d(__webpack_exports__, "c", function() { return __WEBPACK_IMPORTED_MODULE_0__c__["a"]; });
/* harmony namespace reexport (by provided) */ __webpack_require__.d(__webpack_exports__, "x", function() { return __WEBPACK_IMPORTED_MODULE_0__c__["b"]; });
/* harmony namespace reexport (by provided) */ __webpack_require__.d(__webpack_exports__, "y", function() { return __WEBPACK_IMPORTED_MODULE_0__c__["c"]; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__d__ = __webpack_require__(8);
/* harmony reexport (module object) */ __webpack_require__.d(__webpack_exports__, "d", function() { return __WEBPACK_IMPORTED_MODULE_1__d__; });




/***/ }),
/* 5 */,
/* 6 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return c; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__cjs__ = __webpack_require__(7);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__cjs___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__cjs__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__shared__ = __webpack_require__(2);
/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return __WEBPACK_IMPORTED_MODULE_1__shared__["a"]; });
/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "c", function() { return __WEBPACK_IMPORTED_MODULE_1__shared__["b"]; });
// module c


var c = String.fromCharCode(__WEBPACK_IMPORTED_MODULE_0__cjs__["c"].charCodeAt(0) - 2);



/***/ }),
/* 7 */
/***/ (function(module, exports) {

// module cjs (commonjs)
exports.c = "e";

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

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return a; });
// module d
var a = "d";

/***/ })
]);

上述代码可以发现,每个文件都被视为一个module,并且使用了闭包函数把它们分别进行了包裹

webpack3打包代码

只保留模块的代码

// bundle.js
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });

// CONCATENATED MODULE: ./shared2.js
// shared2 module
var y = "y";
// CONCATENATED MODULE: ./shared.js
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return x; });
/* concated harmony reexport */__webpack_require__.d(__webpack_exports__, "b", function() { return y; });
// shared module
var x = "x";


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

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });

// CONCATENATED MODULE: ./a.js
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__shared__ = __webpack_require__(0);
// module a
var a = "a";

// CONCATENATED MODULE: ./b.js
// module b
function b_a() {
	return "b";
};
// CONCATENATED MODULE: ./index.js



__webpack_require__.e/* import() */(0).then(__webpack_require__.bind(null, 3)).then(function(lazy) {
	console.log(a, b_a(), __WEBPACK_IMPORTED_MODULE_0__shared__["a"], __WEBPACK_IMPORTED_MODULE_0__shared__["b"], lazy.c, lazy.d.a, lazy.x, lazy.y);
});
// 0.bundle.js
webpackJsonp([0],[
/* 0 */,
/* 1 */,
/* 2 */
/***/ (function(module, exports) {

// module cjs (commonjs)
exports.c = "e";

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

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });

// CONCATENATED MODULE: ./c.js
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__cjs__ = __webpack_require__(2);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__cjs___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__cjs__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__shared__ = __webpack_require__(0);
// module c


var c = String.fromCharCode(__WEBPACK_IMPORTED_MODULE_0__cjs__["c"].charCodeAt(0) - 2);


// CONCATENATED MODULE: ./d.js
var d_namespaceObject = {};
__webpack_require__.d(d_namespaceObject, "a", function() { return a; });
// module d
var a = "d";
// CONCATENATED MODULE: ./lazy.js
/* concated harmony reexport */__webpack_require__.d(__webpack_exports__, "c", function() { return c; });
/* concated harmony reexport */__webpack_require__.d(__webpack_exports__, "x", function() { return __WEBPACK_IMPORTED_MODULE_1__shared__["a"]; });
/* concated harmony reexport */__webpack_require__.d(__webpack_exports__, "y", function() { return __WEBPACK_IMPORTED_MODULE_1__shared__["b"]; });
/* concated harmony reexport */__webpack_require__.d(__webpack_exports__, "d", function() { return d_namespaceObject; });




/***/ })
]);

从代码上分析,是符合我们上面的图所划分的进行的作用域提升的

结论

通过webpack2和webpack3的所形成的打包文件的对比来看,在代码的体积上,和性能上是有所提升的,对于一个很大型且复杂的项目来说,webpack3的性能速度有所提升和打包后的代码体积更小。

@xcatliu
Copy link

xcatliu commented Jul 13, 2017

看上去不错,期待 webpack3

@JLraining
Copy link

说好的加上耗时对比呢。。。。

@wangning0
Copy link
Owner Author

@JLraining 今天搞!昨天没开机

@wangning0
Copy link
Owner Author

image
image

@ToPeas
Copy link

ToPeas commented Jul 20, 2017

博主有什么文章推荐webpack打包过程和打包出来的文件分析的吗?

@wangning0
Copy link
Owner Author

lcxfs1991/blog#14 @ToPeas

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants