CommonJS
CommonJS是一个规范,最初提出来是在浏览器以外的地方使用,并且当时被命名为ServerJS,后来体现它的广泛性,修改为CommonJS(简称CJS)
- Node是CommonJS在服务器端一个具有代表性的实现
- Browserify是CommonJS在浏览器中的一种实现
- webpack打包工具具备对CommonJS的支持和转换
CommonJS的基本使用
other.js
```javascript const dataList = [1, 3, 6, 3, 2]
function sum(a, b) { return a + b }
module.exports = { dataList, sum }
`main.js`
```javascript
const other = require('./other.js')
console.log(other.dataList); // [ 1, 3, 6, 3, 2 ]
console.log(other.sum(10, 20)); // 30
内部原理: 在other.js中,有一个变量
module
,可以给module.exports
赋值一个对象。 当外部文件通过require()调用时,会获取module.exports
。main.js
中的other和other.js
中的module.exports是同一个对象。
通过exports导出
这种方式不建议使用other.js
const dataList = [1, 3, 6, 3, 2]
function sum(a, b) {
return a + b
}
exports.dataList = dataList
exports.sum = sum
:::danger
注意:不能用exports = { ... }
:::
内部(源码)原理 module.exports = {} exports = module.exports 所以使用
exports = { ... }
不会影响module.exports
require()
require是一个函数,可以帮助我们引入一个文件(模块)中导出的对象。
基本格式:require(X)
情况一:X为核心模块
// fs和path为node的核心模块
const fs = require('fs')
const path = require('path')
情况二:X以’./‘、’../‘、’/‘开头
- 把X当作是一个文件
- 如果X有后缀名,按后缀名的格式查找对应的文件
- 如果X没有后缀名
- 查找文件
X
- 查找
X.js
文件 - 查找
X.json
文件 - 查找
X.node
文件
- 查找文件
- 把X当作是一个目录
- 查找目录下的index文件
- 查找
X/index.js
文件 - 查找
X/index.json
文件 - 查找
X/index.node
文件
- 查找
- 查找目录下的index文件
- 如果没有找到,就报错not find
情况三:一个不是核心模块的普通字符串
会在当前目录下的node_modules
下查找,没有会查找上一级node_modules
,直到查找到根目录
模块加载细节
- 模块在被第一次引入时,模块中的js代码会被运行一次
模块被多次引入时,会缓存,最终只加载(运行)一次 :::info 为什么只能被载一次?
因为模块对象module都有哦一个属性loaded。为true时表示已经被加载过了。 :::循环引入采用深度优先遍历算法加载
执行顺序为:main-A-B-C-G-D-E-F
ES Module
JavaScript没有模块化一直是它的痛点,所以才会产生我们前面学习的社区规范:CommonJS、AMD、CMD等。
ES Module和CommonJS的模块化有一些区别:
- ES Module采用了import和export来导入导出。
- 采用编译器的静态分析和动态引入的方式。
export:负责将模块内的内容导出 import:负责将其他模块的内容导入
ES Module的基本使用
other.js
// syntax: export <declaration statement>
export const msg = "hello"
export function foo() {
console.log("foo")
}
main.js
// 导入外部文件的内容
import {msg, foo} from "./other.js";
console.log(msg)
foo()
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!-- 需要加上type="module" -->
<script src="./main.js" type="module"></script>
</body>
</html>
错误案例分析
- 缺少
type="module"
的情况:
- 直接在本地打开文件(用浏览器运行文件,而不是服务)
这个在MDN上面有给出解释: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Modules 你需要注意本地测试 — 如果你通过本地加载Html 文件 (比如一个 file:// 路径的文件), 你将会遇到 CORS 错误,因为 Javascript 模块安全性需要。 你需要通过一个服务器来测试。
- VSCode,VSCode中有一个插件:
Live Server
- WebStorm:以浏览器打开时会自动启动一个服务器
ES Module 导出
export的多种用法
- 在语句声明的前面直接加上export关键字
export const msg = "hello"
。 将所有需要导出的标识符,放到export后面的 { }中。\ :::danger 注意:这里的
{ }
里面不是ES6的对象字面量的增强写法,{ }
也不是表示一个对象的;
所以: export {name: name},是错误的写法;
:::导出时给标识符起一个别名 。
const name = "foo" export { name as newName }
ES Module 导入
import的多种用法
导入所需的
import {b} from 'a'
- 通过
*
导入一个模块中所有导出的内容import * as b from 'a'
- 导出时可以起别名
import { b as c } from 'a'
ES Module 导入和导出的结合使用
在开发和封装一个库时, 通常我们希望将暴露的所有接口放到一个文件中;这样方便指定统一的接口规范,也方便阅读; 这个时候,我们就可以使用export和import结合使用。 ```javascript // 将模块a的内容全部导出 export * from ‘a’
// 将模块b的c导出 export { c } from ‘b’
// 将模块d中的e重命名为f并导出 export { e as f} from ‘d’
<a name="xp48M"></a>
### ES Module default关键字
默认导出export时可以不需要指定名字;在导入时不需要使用 {},并且可以自己来指定名字; 它也方便我们和现有的CommonJS等规范相互操作;
<a name="dg9JX"></a>
#### 默认导出方式一:export default
`other.js`
```javascript
export const msg = "hello"
export function foo() {
console.log("foo")
}
function bar() {
console.log("bar")
}
// 默认导出
export default bar
main.js
import {msg, foo} from "./other.js";
// 默认导出,可以自己起名
import baz from './other.js'
console.log(msg)
foo()
baz()
结果:
:::warning
注意:在一个模块中,只能有一个默认导出(default export);
:::
默认导出方式二:export { a as default }
const msg = "hello"
function foo() {
console.log("foo")
}
function bar() {
console.log("bar")
}
// 效果同方式一
export {
msg,
foo,
bar as default
}
import()函数
通过import加载一个模块,是不可以在其放到逻辑代码中的
为什么会出现这个情况呢?
- import是同步加载
- 这是因为ES Module在被JS引擎解析时,就必须知道它的依赖关系;
- 由于这个时候js代码没有任何的运行,所以无法在进行类似于if判断中根据代码的执行情况;
:::info
import()
函数是异步加载,返回Promise
:::
const flag = true
if (flag) {
import('./a.js').then(res => {
// res为Module对象,可以通过res.property获取模块内容
console.log(res)
})
} else {
import('./b.js').then(res => {
console.log(res)
})
}
import meta
import.meta
是ES11中新增的特性。 一个给JavaScript模块暴露特定上下文的元数据属性的对象。它包含了这个模块的信息,比如说这个模块的URL;