ES6中import的循环依赖的理解

ES6 循环依赖的一个例子

a.js

1
2
3
4
5
6
import {bar} from './b.js';
export function foo() {
bar();
console.log('执行完毕');
}
foo();

b.js

1
2
3
4
5
6
import {foo} from './a.js';
export function bar() {
if (Math.random() > 0.5) {
foo();
}
}

babel-node a.js -> 执行完毕

  1. import {bar} from ‘./b.js’ -> 此时对b模块只是一个引用, 不会去执行
  2. foo() -> bar() -> 执行b
  3. import {foo} from ‘./a.js’ -> 指向a.js 中的foo对象
  4. Math.random() > 0.5 条件成立执行 foo 跳到2, 否则 不执行, 直接执行5
  5. 执行完毕

ES6处理循环依赖

ES6根本不关心会不会发生循环依赖, 只是生产一个指向被加载模块的一个引用, 这和CommonJS不同

上述代码webpack编译后如下:

编译后源码完整
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
(function(modules) {
var installedModules = {};
function __webpack_require__(moduleId) {
if (installedModules[moduleId]) {
return installedModules[moduleId];
}

var module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
}
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
module.l = true;
return module.exports;
}

__webpack_require_.d = function(exports, name, getter) {
if(!__webpack_require__.o(exports, name)) {
Object.defineProperty(exports, name, { enumerable: true, get: getter });
}
};

__webpack_require__.r = function(exports) {
if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
}
Object.defineProperty(exports, '__esModule', { value: true });
};

...
return __webpack_require__(__webpack_require__.s = "./test/index.ts");
})({
'./test/a.ts': (function(module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, "foo", function() { return foo; });
var _b__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./b */ "./test/b.ts");

function foo() {
Object(_b__WEBPACK_IMPORTED_MODULE_0__["bar"])();
console.log('foo 执行完毕');
}
foo();
}),
'./test/b.ts': (function(module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, "bar", function() { return bar; });
var _a__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./a */ "./test/a.ts");

function bar() {
if (Math.random() > 0.5) {
Object(_a__WEBPACK_IMPORTED_MODULE_0__["foo"])();
}
}
}),
'./test/index.ts': (function(module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
var _a_ts__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./a.ts */ "./test/a.ts");
})
})
1
2
// 说明当前module是使用ES6模块
Object.defineProperty(exports, '__esModule', { value: true });

ES6 输出值的引用

a.js

1
2
3
4
5
6
7
8
import {foo} from './b';
console.log(foo);
setTimeout(() => {
console.log(foo);
import('./b').then(({foo}) => {
console.log(foo);
});
}, 1000);

b.js

1
2
3
4
export let foo = 1;
setTimeout(() => {
foo = 2;
}, 500);
  1. babel-node a.js
  2. -> 1 -> 2 -> 2
编译后源码完整
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
(function(modules) {
var installedModules = {};
function __webpack_require__(moduleId) {
if (installedModules[moduleId]) {
return installedModules[moduleId];
}

var module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
}
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
module.l = true;
return module.exports;
}

__webpack_require__.r = function(exports) {
if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
}
Object.defineProperty(exports, '__esModule', { value: true });
};

...
return __webpack_require__(__webpack_require__.s = "./other/index.ts");
})({
'./other/a.ts': (function(module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
var _b__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./b */ "./other/b.ts");

console.log(_b__WEBPACK_IMPORTED_MODULE_0__["foo"]);
setTimeout(() => {
console.log(_b__WEBPACK_IMPORTED_MODULE_0__["foo"]);
Promise.resolve(/*! import() */).then(__webpack_require__.bind(null, /*! ./b */ "./other/b.ts")).then(({ foo }) => {
console.log(foo);
});
}, 1000);
}),
'./other/b.ts': (function(module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, "foo", function() { return foo; });
let foo = 1;
setTimeout(() => {
foo = 2;
}, 500);
}),
'./other/index.ts': (function(module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
var _a_ts__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./a.ts */ "./other/a.ts");
})
})

ES6模块的特性

  1. import 命令会被javascript 引擎静态分析, 先于模块内的其它内容执行
  2. export 命令会有变量声明提前的效果

a.js

1
2
console.log('a.js')
import { foo } from './b';

b.js

1
2
export let foo = 1;
console.log('b.js 先执行');

执行结果

1
2
b.js 先执行
a.js

参考文章