webpack series 1--circular dependency analysis and processing summary

Get into the habit of writing together! This is the second day of my participation in the "Nuggets Daily New Plan · April Update Challenge"

What is a circular dependency?

In general, development does not pay much attention to it, nor does it encounter circular dependencies. However, as the complexity of the project increases, especially for large projects with complex dependencies, circular dependencies are prone to occur, and unexpected problems are likely to occur, so In the future in the coding process, we must strengthen the awareness of this aspect.

The so-called circular dependency, in simple terms, means that a.js depends on b.js and the script execution of b also depends on a.js.

Explain what circular dependencies are with a demo:

webpack.config.js

module.exports = {
    entry: './main.js',
}
复制代码

mian.js

import a from './a';\
console.log(a);
复制代码

a.js

import b from './b';
export default b;
复制代码

b.js

import a from './a';
console.log('a:', a); 
export default 1;

复制代码

 The entry file for  executing the compiled code image.png webpack is main.js , and then their references are: main.js –> a.js –>  b.js –> a.js –> b.js

The problem is that  a.js  is referenced circularly . The reference value  of a.js  imported in  b.js is undefined , although we want it to get the last  1  result.

Why circular dependencies occur

The entire demo above will be compiled into the following code by webpack

image.png

This is an immediate execution function. When the browser loads js, it will be triggered to execute. The parameter of the immediate function is an object, the key is the dependent file path, and the value is the compiled content of the file.

And pass in the entry file main.js as a parameter, execute the __webpack_require__ method, and start the execution of the entire project code

image.png

In this function, the following things are done

1、判断全局 installedModules 是否有对应的 moduleId 值,有的话就导出其引用

2、如果没有,就执行 modules[moduleId]  方法,最后返回该模块 module 的引用

也就是说它会执行对应依赖里面的代码,执行后将该依赖再保存到installedModules对象里,下次就不要再执行了

image.png

1、初次调用的时候,installedModules[moduleId]  为空,接着通过 modules[moduleId].call 执行对应的依赖代码。第一个被执行的就是入口文件main.js的代码

2、main.js 里导入了 a.js,则又会调用  webpack_require  方法

3、这里对 a.js 的调用并没有结束(返回值还没有拿到),由于 a.js 中又导入了 b.js,所以又会同上述步骤再执行 b.js 对应的函数。

4、但执行到b这里时就不同了( modules[b.js].call )

5、b.js 文件中导入了 a.js 文件,导致 a.js 作为参数又会进入到  webpack_require  方法。

6、因为先前 webpack_require(a.js)  被执行过了,所以在 modules[b.js].call 环节执行 webpack_require(a.js)  中,installedModules[moduleId]  判断为 true 了:

7、而最开始第一次导入 a.js 时,其返回值还没有拿到,所以此时为 true 后,返回值就为 undefined ,这就是问题的出现原因。

image.png

怎么解决?

总结3种处理循环依赖的方法(也欢迎各位小伙伴补充)

  • 单独export一个方法 ⭐️⭐️⭐️
    • 这种适合一个巨无霸对象,某个属性的方法被别的文件引用,别的文件引用的时候是只能全量引入bigObject,然后这样使用bigObject.a(),这种情况下我们可以将方法a,单独抽离出来并export出去,减少循环依赖并使产出最小。
  • 将功能尽可能小的封装在一起,需要某个功能直接按路径引用⭐️⭐️
    • 这种也是适合一个巨无霸对象,但是某个属性的功能是集成了其他对象的某个方法,别的文件引用的时候也只能全量引入bigObject,这种情况下,我们可以将这种功能单独抽离出一个js,然后用路径的方式去引用import a from bigObject/a.js,这样也能减少循环依赖的情况,可能使产出最小。
  • 最小复制原则 ⭐️
    • 有时候循环依赖的环可能是比较长的,我在重构的时候遇见的最长的依赖环是10个文件组成一个闭环,但是如果环中只是因为大家共同引用了一个纯函数工具方法,那我们就干脆霸道的将这个方法复制一份放到某个目录下就好了,推荐指数一颗星。

未雨绸缪:介绍一个插件

circular-dependency-plugin

能够在编译过程中检测项目代码中是否存在循环依赖,如果存在循环依赖就会再控制台输出警告,建议再开发环境下可以配置一个,能够未雨绸缪,毕竟有时候当循环依赖的闭环太长的时候,肉眼是很难区分出来的。

image.png

欢迎小伙伴们补充

以上3种方法是实践中我用来处理循环依赖的,欢迎有其他方法的小伙伴补充哈

Guess you like

Origin juejin.im/post/7084600958524588040