CommonJS和ES6 Module分别如何处理循环依赖的问题?
前篇(CommonJS和ES6 Module的区别):https://juejin.cn/post/6959360326299025445
前言:一般来说工程中应该尽量避免循环依赖的产生,因为从软件设计的角度来说,单向的依赖关系更加清晰,而循环依赖则会带来一定的复杂度。
- 但工程的复杂度上升到足够规模时,就容易出现隐藏的循环依赖关系。简单来说,A和B两个模块之间是否存在直接的循环依赖关系是很容易被发现的。但实际情况往往是A依赖于B,B依赖于C,C依赖于D,最后绕了一大圈,D又依赖于A。当中间模块太多时就很难发现A和B之间存在着隐式的循环依赖。
实际代码举例
目录结构:
├── main.js
├── bar.js
├── aaa.js
commonjs 循环依赖的处理情况(注释内有解析)
// main.js
const bar = require('./bar.js')
console.log('当前是main.js内:', bar) // {}
module.exports = '在main.js内'
// bar.js
const main = require('./main.js')
console.log('当前是bar.js内:', main)
module.exports = 'bar.js内'
// 执行 node ./main.js , 输出:
当前是bar.js内: {} // 解析:执行到bar.js内时,main.js还没有执行完,就没有东西导出,会默认导出空对象
当前是main.js内: bar.js内
es6 module 循环依赖的处理情况(注释内有解析)
// main.js
import bar from './bar.js'
console.log('当前是main.js内:', bar)
export default '在main.js内'
// bar.js
import main from './main.js'
console.log('当前是bar.js内:', main)
export default 'bar.js内'
// 执行 webpack-dev-server xx 启服务去执行 入口是main.js , 输出:
当前是bar.js内: undefined // 解析:编译到bar.js内时,main.js还没有编译完,就没有东西编译映射到main这个变量上去,就是undefined
当前是main.js内: bar.js内
解决方法
只能用es6 module,因为只有他的值的动态映射的,可以得到变更的状态
核心思路
- es6 module导出的值是动态映射的
- 用function包裹,让加载先不立即执行
- 用一个变量,来解决重复调用的问题(闭包)
// main.js (为了打包方便,此处main.js只作为入口,aaa和bar循环依赖)
import aaa from './aaa.js'
aaa('main')
// aaa.js
import bar from './bar.js'
function aaa (name) {
console.log('当前是aaa.js内:', name)
bar('aaa')
}
export default aaa
// bar.js
import aaa from './aaa.js'
let time = false
function bar (name) {
if (!time) {
time = true
console.log('当前是bar.js内:', name)
aaa('bar')
}
}
export default bar
// 执行 webpack-dev-server xx 启服务去执行 入口是main.js , 输出:
当前是aaa.js内: main
当前是bar.js内: aaa
当前是aaa.js内: bar
参考《Webpack实战:入门、进阶与调优》(居玉皓)