JavaScript模块化总结(三)

126 阅读3分钟

「这是我参与2022首次更文挑战的第17天,活动详情查看:2022首次更文挑战」。

在上篇文章中,我们讲到了CommonJS的基本使用以及模块化的导出 JavaScript模块化总结(二)。在本篇文章,我们介绍模块化的导入以及CommonJS的缺点。

require

require是一个函数,它可以帮助我们导入其他文件(模块)导出的对象。那么require的导入规则是什么呢?

导入格式:require(X)

情况一:如果X是node的核心模块,例如http,axios。

那么会直接返回核心模块,并且停止查找。

情况二:X是以./或者../或者/开头的

第一步: 将X当做一个文件,在对应的目录中查找。

(1)如果有后缀名,按照后缀名的格式查找对应的文件

(2)如果没有后缀名,会按照以下顺序添加后缀名:

直接查找文件X =》查找X.js文件 =》查找X.json文件 =》查找X.node文件

第二步:在第一步没有找到对应的文件,会将X作为一个目录。

查找X目录下的index文件:

查找X/index.js文件 =》查找X/index.json文件 =》查找X/index.node文件

以上两步都没有找到,那么就会报not found的错误。

情况三:直接是X,既不是情况一中的核心模块,也不是情况二的路径。

会在当前文件夹下的node_modules文件夹去查找X文件夹,然后它会自动拼接X/index.js,去找这个文件,如果没有找到,会去上一层的node_modules文件夹中去查找,往上一层层地去node_modules文件夹中查找。如果找不到,就报错。

模块的加载过程

(1)模块在被第一次引入时,模块中的js代码会被运行一次

(2)模块被多次引入时,会缓存,最终只加载(运行)一次

// foo.js文件
const name = 'haha'
const age = 18
console.log('foo:', name);
console.log('foo中的代码被运行');

module.exports = {
  name,
  age
}
// main.js文件
console.log('main.js代码开始运行');
require('./foo')
require('./foo')
require('./foo')

console.log('main.js代码后续运行');

我们可以看到,在main.js中虽然多次引入了foo模块,但是foo模块只运行了一次。

image.png (3)如果是循环引入,那么加载顺序是采用深度优先算法

CommonJS规范缺点

CommonJS加载模块是同步的,这意味着必须等到对应的模块加载完毕,才能运行当前模块的内容。

在服务器中应用CommonJS不会有问题,因为服务器加载的js文件都是本地文件,加载速度非常快。但是浏览器加载js文件需要先从服务器将文件下载下来,然后再加载运行。浏览器中还有一些DOM操作,如果同步的话就意味着后续的js代码都无法运行。

所以在浏览器中我们通常不使用CommonJS,但是在webpack中我们会使用CommonJS,因为webpack会将我们的代码转成浏览器可以直接执行的代码。

目前的浏览器已经支持ES Modules了,另外借助于webpack等工具可以实现对CommonJS或者这ES Module代码的转换。