深入理解模块化开发

142 阅读3分钟

为什么需要模块化?

首先模块化是什么?我个人的理解就是一个作用域,在这个作用域内的变量,方法不会受到其他环境影响,接下来我来看一段代码。

foo.js

var names = 'kobe'
console.log(names)

main.js

console.log(names)

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script src="./foo.js"></script>
  <script src="./main.js"></script>
</body>
</html>

我们去打开index.html发现会输出两个kobe,不是单独引入了两个js文件?其实在js中是没有模块化这么一说,那假如我在开发foo.js,小王在开发main.js,那一些变量命名重复怎么办或者一些函数都命名一样。这上线不就是bug爆了,这么看来模块化是非常有必要的,毕竟自己写的代码不想影响到别人。 对于模块化自然就有了些规范,CommonJS,AMD,CMD,ES6模块化,好那就依次来验证一下模块化的好处。

CommonJS和Node

CommonJS有自己的一套规范,Node是 CommonJS在服务端的一种体现。

  1. 在node中每个文件就是一个单独的模块
  2. exportsmodule.exports可以负责对模块中的内容进行导出
  3. require函数可以帮助我们导入其他模块

exports

foo.js

let user = 'kobe'

let age = 41

function say(name) {
  return `${name}你好`
}

exports.user = user
exports.age = age
exports.say = say('kobe')

main.js

let bar = require('./foo')

console.log(bar)

在foo.js打印exports在main.js中打印bar,发现两者打印内容一样,其实node中实现commonJS的本质就是对象的引用赋值,require函数的作用就是把exports对象进行拷贝(注意这是浅拷贝)

module.exports

  1. CommonJS中是没有module.exports的概念的
  2. Node中为了实现模块的导出使用的是Module的类,每一个模块都是Module的一个实例 Node中真正用于导出的其实根本不是exports,而是module.exports,并且内部是将exports赋值module.exports

require的查找规则

首先我们require(X)

  • X是一个模块(path,http),直接返回模块,停止查找
  • X是一个文件(./ , ../, /开头)
    1. 如果有后缀名,则返回后缀名文件
    2. 如果没有后缀名,则按以下顺序查找
      1. 直接查找文件X
      2. 查找X.js文件
      3. 查找X.json文件
      4. 查找X.node文件
  • 没有找到对应的文件,将X作为一个目录
    1. 查找X/index.js文件
    2. 查找X/index.json文件
    3. 查找X/index.node文件
    • 如果没有找到,那么报错:not found
  • 直接是一个X(没有路径),并且X不是一个核心模块。那么它将向上级目录查找node_modules文件。直到查找到根目录下。如果上面的路径中都没有找到,那么报错:not found。

模块的加载过程

  • 模块在被第一次引入时,模块中的js代码会被运行一次
  • 模块被多次引入时,会缓存,最终只加载(运行)一次 每个模块对象module都有一个属性:loaded。为false表示还没有加载,为true表示已经加载
  • 如果有循环引入,那么加载顺序是什么?

QQ截图20220501094557.jpg

main.js -> a.js -> c.js -> d.js -> e.js -> b.js

Node采用的是深度优先算法去加载

CommonJS规范缺点

  • CommonJS加载模块是同步的:

    • 同步的意味着只有等到对应的模块加载完毕,当前模块中的内容才能被运行。
    • 这个在服务器不会有什么问题,因为服务器加载的js文件都是本地文件,加载速度非常快。

ES Module的解析过程

对于ES模块,分为三个步骤:

  • 构建 — 查找、下载并将所有文件解析为模块记录。
  • 实例化 — 对模块记录进行实例化,并且分配内存空间,解析模块的导入和导出语句,把模块指向对应的内存地址。
  • 运行 — 运行代码,计算值,并且将值填充到内存地址中。 详情请点击

以上是近期参考一些视频和文字作出的总结,加深自己对模块化的了解。