起因是开发时的一个报错信息:Cannot access '__WEBPACK_DEFAULT_EXPORT__' before initialization

由于报错信息不明确,网上也搜不到明确的原因解释和解决方法,所以自行排查了很久才逐渐找到原因。要说怎么排查,就是最笨的也是最有效的“代码删除法”,即从入口文件开始一行行删代码,直到定位到具体出错位置,然后凭借自身知识和经验判断出问题原因。

原因就是import的循环引用导致webpack无法正确解析。

一、循环引用简介

最简单的 a 引用了 b,b 又引用了 a,这就产生了循环引用。
复杂点的无非就是链路长一些,例如 a -> b -> c -> d -> a。

循环引用可能会导致内存栈溢出。但也不是一定会有问题,比如 b 导出了两个方法,a 引用的是 b 导出的方法 fn1,而 b 是在方法 fn2 里引用的 a,这种情况其实是不会有问题的。

但由于可能的风险,且难以发现,所以编写代码时还是尽量规避使用循环引用。

二、在项目中排查

利用 webpack 的插件 circular-dependency-plugin 来排查项目中的循环引用问题非常方便。
(并非所有的循环引用都会有问题,所以这个插件建议在需要排查问题时使用,开发时是否开启看个人需要。)

  1. 安装插件:
npm i circular-dependency-plugin -D
  1. 在 webpack 配置文件里添加 plugins 配置。
const CircularDependencyPlugin = require('circular-dependency-plugin')export default {...,plugins: [...,new CircularDependencyPlugin({exclude: /node_modules/,include: /src/,failOnError: false,allowAsyncCycles: false,cwd: process.cwd(),}),]
}
  1. 重启项目,在启动命令行里就能看到循环引用的警告信息,插件会帮你定位出问题的文件路径。

三、esm的引用处理

ESM(es modules,即es6模块化)是编译时加载,和commonJs的运行时加载不同。esm是尽量的静态化,编译时就能确定模块的依赖关系,正因为此,esm能够通过静态分析进行tree shaking优化。

代码示例:

  • main.js
import a from './a'
console.log(a)
  • a.js
console.log('执行a')
import b from './b'
console.log(b)
console.log('导入b之后')
export default '我是a模块'
  • b.js
console.log('执行b')
import a from './a'
console.log(a)
console.log('导入a之后')
export default '我是b模块'
  • 执行main.js,输出结果:
// main.js里import了a,先进到a解析
// import语句提前解析,a里import了b,所以暂停a的解析,进到b解析
// b里import了a,但a此时还未执行完,拿到的a值是undefined,然后往下继续解析b
b.js:1 执行b
b.js:3 undefined
b.js:4 导入a之后// b解析完并拿到了b的导出结果,开始回到a里继续往下解析
a.js:1 执行a
a.js:3 我是b模块
a.js:4 导入b之后// a解析完后,回到main.js解析执行
main.js:2 我是a模块

esm的import命令是在编译阶段就执行,优先于自身内容执行。
esm并不关心是否存在循环引用,只是生成一个指向被加载模块的引用,代码未执行时,这个引用的值就是undefined。

四、webpack 的引用处理

即使 esm 里循环引用没有异常,在webpack编译时可能也会报错,毕竟webpack会把 esm 降级为 es5,降级处理方式上可能会有一定的差异。

同样的上述示例代码,webpack(v5.64.4)处理后在执行console.log(a)语句时就会出现异常报错:Cannot access '__WEBPACK_DEFAULT_EXPORT__' before initialization

  • esm的处理方式是,先静态分析import,然后动态export导出(导出引用)。
  • webapck的处理方式是,先将所有export提到了模块的开始,然后import提升。

五、解决方法

1、引用抽离

就是把有循环引用的地方抽离到另一单独文件里,换句话说就是不使用循环引用。

2、导出函数

把之前默认的导出对象改成导出函数的形式,从函数返回值里取导出结果。
由于每一个函数都会形成一个单独的局部作用域,不同的作用域有着不同的数据引用地址。
这样每次引入的结果都是一个新的引用,不会冲突,这种情况webpack也能正常的处理。


参考链接:
https://blog.csdn.net/wu_xianqiang/article/details/100705034
https://zhuanlan.zhihu.com/p/141863544?from_voters_page=true
https://zhuanlan.zhihu.com/p/33049803

es6模块循环引用的问题相关推荐

  1. es6 循环加载ES6模块

    循环加载ES6模块 "循环加载"(circular dependency)指的是,a脚本的执行依赖b脚本,而b脚本的执行又依赖a脚本. // a.js var b = requir ...

  2. ES6模块的循环加载

    先理解JS代码执行的过程. 1.引擎:负责整个JavaScript编译及执行的过程2.编译器:负责语法分析和代码生成3.作用域:负责收集并维护所有声明的标识符组成一系列的查询系统,确定当前执行的代码对 ...

  3. maven依赖循环引用_Maven说我在多模块项目中有一个循环引用,但无法弄清楚原因...

    我有一个多模块项目,看起来像这样: 模块1 的pom.xml 模块2 的pom.xml 的pom.xml module2中的pom.xml依赖于module1. 当我运行mvn clean compi ...

  4. es6 依赖循环_探索 JavaScript 中的依赖管理及循环依赖

    我们通常会把项目中使用的第三方依赖写在 package.json 文件里,然后使用 npm .cnpm 或者 yarn 这些流行的依赖管理工具来帮我们管理这些依赖.但是它们是如何管理这些依赖的.它们之 ...

  5. es6 混合commjs_详谈commonjs模块与es6模块的区别

    到目前为止,已经实习了3个月的时间了.最近在面试,在面试题里面有题目涉及到模块循环加载的知识.趁着这个机会,将commonjs模块与es6模块之间一些重要的的区别做个总结.语法上有什么区别就不具体说了 ...

  6. ES6模块与commonJS模块的差异

    参考: 前端模块化 ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,旨在成为浏览器和服务器通用的模块解决方案. 其模块功能主要由两个命令构成:export和import.export命 ...

  7. 详解CommonJS模块与ES6模块

    详解CommonJS模块与ES6模块 历史上,JS一直没有模块体系,在ES6之前,最主要的是CommonJS和AMD两种.前者用于服务器,后者用于浏览器,ES6在语言标准的层面上实现了模块功能,使用简 ...

  8. ES6 模块加载export 、import、export default 、import() 语法与区别,笔记总结

    ES6模块加载export .import.export default .import() 语法与区别 在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种. ...

  9. 从循环引用谈依赖倒置原则

    在业务开发中,通常会按照业务或者逻辑将项目分成好几个工程文件以方便重用和模块化,有时候我们分开的两个项目可能存在相互引用的情况,举个例子,比如有两个系统,订单系统和产品系统,订单系统需要从产品系统中了 ...

最新文章

  1. Outlook Hotmail Connector
  2. 判断三角形java代码_打基础之LeetCode算法题第72篇:最大的三角形周长问题
  3. python的可变长参数
  4. 20分钟打造你的Bootstrap站点
  5. Windows之在终端打开当前目录的命令
  6. 单元测试debug过程中,显示variables are not available
  7. 4.线性和卷积——线性滤波器、非锐化掩蔽原理揭破_4
  8. 【MySQL】MySQL RROR 3680 (HY000): Failed to create schema directory ‘db2019‘ (errno: 2 - No such file
  9. visual studio 容器工具首次加载太慢 vsdbg\vs2017u5 exists, deleting 的解决方案
  10. day13-面向对象
  11. python urllib3离线安装_离线安装spyder的Python环境
  12. 很简单的源码剖析-SpringBoot内嵌Tomcat原理
  13. limbo镜像linux下载,Limbowin10镜像下载|Limbo模拟器win10镜像 可上网版下载_最火手机站...
  14. 一、(3) 结巴分词
  15. 【信号处理】音频信号处理平台含Matlab源码
  16. JS 实现复制粘贴功能
  17. 用计算机研究唐诗,妙哉!用文言文编程 竟从 28 万行唐诗中找出了对称矩阵
  18. 使用AVSpeechSynthesizer添加文本转语音的功能
  19. ACM-ICPC 2018 沈阳赛区网络预赛 F. Fantastic Graph (有上下界可行流)
  20. 美团java研发岗二面:mysql功能介绍

热门文章

  1. 数据挖掘机器学习及其他领域数据集汇总
  2. 疯狂java笔记(七) - Java集合之Map
  3. Linux内存uncache区域拷贝优化
  4. [Vulkan教程]绘制一个三角形/呈现/交换链(Swip chain)
  5. 谷歌金融 Onebox 实现实时信息更新
  6. 学周刊杂志学周刊杂志社学周刊编辑部2022年第24期目录
  7. 模糊数学在计算机方面的应用,模糊数学理论在图像处理中的应用
  8. java打开jnlp_如何打开jnlp
  9. java jnlp 运行_jnlp——通过浏览器直接执行java应用程序
  10. Spark性能优化之-资源调优