Published on
733

js import 循环引用带来的问题

Authors
  • avatar
    Name
    小辉辉
    Twitter

前言

对于现在流行js模块化开发项目的时代,大多数新手前端开发人员一上来可能就使用上了公司配套的相关的脚手架,其中模块导入导出一般使用es6的import语法,附带了eslint开发规范。

但是在开发过程中,由于代码组织结构已开始没有想好,或者想重复利用某些业务逻辑时,时常会遇到eslint提示的循环引用警告,有经验的开发都知道这种情况应该直接避免的,但是有些开发人员出于方便,直接使用eslint禁止的注释绕过了这个警告,殊不知到,这种做法很有可能会在项目里埋下一颗定时炸弹。

问题

入口文件 main.ts

import {square} from './a.ts'

square(333); 

文件 a.ts

export { square } from './b';

export const qq = [1, 2, 3];

文件 b.ts

import { qq } from './a';

export const square = (x) => x * x;

console.log(qq[0]);

试问最后的运行结果是什么,下面给出三个选项供大家选择:

  • A: 1 9
  • B: 报错 Cannot read properties of undefined (reading '0')
  • C: 9 1

分析

首先揭晓答案是B,页面会直接输出错误。

我按照个人的思路来分析下,如果有不对的地方还请各位老师多多指教了。

  1. 首先加载了文件a

  2. 文件a按照顺序先执行了引入文件b的操作

  3. 这时候文件b又去引用文件a

  4. 这时我们会发现文件a已经被加载了,那么此刻会直接从模块缓存中去获取文件a导出的变量qq

  5. 但是考虑到前面文件a的加载流程,其实还没有到执行到定义qq变量这个阶段

  6. 那自然而然的这时候的变量qq就是undefined

其实更好理解的方式是直接看编译器转换es6的import语句为最后输出的代码块,这样理解起来就会更加清晰了,当然这个任务就交给各位老师自行去看了。

写到这里肯定很多人会奇怪,为什么以前一直循环引用就没有报错呢?那其实是因为文件b里使用文件a的方式有关,如果你引用的b是延时执行,例如放到一个函数里面去返回,等到调用的时候,其实qq这个变量已经定义执行结束了,那这样就不会有问题。

总结

通过上面的一个简单的例子,来说明了平时开发过程中引用循环写法存在的危害,我们在开发过程中还是要遵循eslint推荐的规则,这样子项目跑起来才更加的健壮,禁得起时间的考验。