【重读JS高程4(红宝书)】第26章-模块化

120 阅读3分钟

1、Commonjs规范

CommonJS 规范主要用于Node服务端,它的导入、导出都是同步任务,因为所有资源在服务端的文件系统中,无需考虑网络延迟。CommonJS 模块语法不能在浏览器中直接运行。

导入和导出

导出的本质就是向 module 对象上添加属性,而后 module 对象会作为一个立即执行函数的返回值,这样就实现了向外暴露

// commonjs-b.js
module.exports = {
  name: "高圆圆",
};

导入的本质就是用 require() 函数读取文件,Node 根据文件的绝对路径找到 commonjs-b.js,用 fs.readFileSync() 读取,然后将结果放入一个立即执行函数中,commonjs-b.js 向外暴露的 module 对象 就是立即执行函数的返回值,这就是为何我们使用 require() 函数能实现模块导入的原因

// commonjs-a.js
let data = require("./commonjs-b");

console.log("data", data);
// { name: '高圆圆' }

特点

动态性: 因为 require() 函数是个同步任务,所以你既可以在作用域顶部使用它,也能在代码中只用它

let data = require("./commonjs-b");

if (true) {
  let data = require("./commonjs-b");
}

单例性:require() 函数导入的模块,永远是单例的,模块第一次加载后会被缓存,后续加载会取得缓存的模块

let data1 = require("./commonjs-b");
let data2 = require("./commonjs-b");

console.log(data1 === data2);
// true

导出值的本质:类似深拷贝,你可以在当前模块中随意修改,不影响原始模块

let data = require("./commonjs-b");

data.name = "周子琰";

console.log("data.name", data.name);
// 周子琰

2、ESM规范

带有 type="module"属性的

<script type="module" src="./ESM-02.js"></script>
```
// ESM-01.js

export let name = "高圆圆";

export let obj = {
  age: 18,
};
// ESM-02.js

import { name } from "./ESM-01.js";
import { obj } from "./ESM-01.js";

console.log("name", name);
console.log("obj", obj);

特点

import自动提升

因为 ESM 的 import 是异步的,import 会被自动提升到作用域的顶部,因此它能支持静态分析,在编译阶段就能确定模块之间的依赖关系,这也是 tree shaking的基础

动态导入 import() 函数

但这样就会导致一个问题,ESM 无法像 Commonjs 一样在代码中动态导入的模块,所以2018年推出了 import() 函数,它返回一个 Promsie ,完美解决 ESM 无法动态导入模块的问题。

import() 函数的典型应用:路由懒加载,配置路由规则时,用 import() 引入组件,再结合Webpack、Vite 等打包工具的代码拆分能力,实现路由的按需加载

单例性

import 导入的值,也是单例的

import { name } from "./ESM-01.js";
import { name as name2 } from "./ESM-01.js";

console.log(name === name2);
// true

导出值的本质

都是引用。如果导出的值是基本类型,在当前模块中,无法修改;如果导出的值是引用类型,不能修改地址,但能修改对象中的属性

export let name = "高圆圆";

export let obj = {
  age: 18,
};
import { name } from "./ESM-01.js";
import { obj } from "./ESM-01.js";

// 导出值的本质

// 1、不能修改基本类型, 报错:ESM-02.js:15 Uncaught TypeError: Assignment to constant variable.
// name = "123";

// 2、能修改引用类型中的属性
obj.age = 28;
console.log("obj", obj);

源码见仓库:github.com/adamswan/Re…