前言
什么是模块化?
根据功能或业务将一个大程序拆分成互相依赖的小文件,再用简单的方式拼装起来;
一个模块(module)就是一个文件。一个脚本就是一个模块
为什么要模块化?
如果没有模块化,则所有script标签必须保证顺序正确,否则会依赖报错,全局变量存在命名冲突,占用内存无法被回收,IIFE/namespace会导致代码可读性低等诸多问题。
CommonJS规范
导出模块写法
//方式1✔️
module.exports = {
a: 'Hi',
b: 'Axjy'
}
//方式2✔️
module.exports.a = 'Axjy';
//方式3✔️
exports.a = 'Axjy';
//错误写法❌
exports = {
a: 'Hi',
b: 'Axjy'
}
为什么最后一种写法是错的?
因为Node为每个模块提供一个exports变量,指向module.exports。这等同在每个模块头部,有一行这样的命令。
造成的结果是,在对外输出模块接口时,可以向exports对象添加方法。
但是不能直接将exports变量指向一个值,因为这样等于切断了exports
与module.exports
的联系,导致exports不再指向module.exports
加载方式:
- 加载内置模块
require('fs')
【Node.js自带的模块】 - 加载(相对/绝对)路径的文件模块
require('/User/../file.js')
require('./file.js')
- 加载npm包
require('lodash')
【即通过npm i lodash
下载到node_modules
里的模块】
查找原则
内置模块:直接跳过路径分析和文件定位
路径模块:直接得出相对路径
npm包查找原则:
-
当前目录node_ modules
-
如果没有,父级目录的node_ modules
-
如果没有,沿着路径向上递归,直到根目录下node_ modules
-
找到之后会加载package.json main指向的文件,如果没有package.json则依次查找
index.js、index.json、index. node
注:
-
require()
是node
中的全局方法,所以不能直接在html里面用; -
require不是CommonJS独有,CommonJS只是众多规范中的其中一种
-
require()
的参数可以是变量或做字符串拼接
CommonJS规范的特点
-
所有代码都运行在模块作用域,不会污染全局作用域。
-
模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
-
模块加载的顺序,按照其在代码中出现的顺序。
require.cache中缓存着加载过的模块,缓存的原因:同步加载
- 文件模块查找耗时,如果每次require都需要重新遍历查找,性能会比较差;
- 在实际开发中,模块可能包含副作用代码
ES Modules (ESM)
ES Modules (ESM),语言层面的模块化规范,与环境无关,可借助babel编译
- ESM是在ES6语言层面提出的一种模块化标准;
- ESM中主要有import、export 两个关键词,不能console打印两个关键词
在声明前导出
以下的导出均有效
// 导出数组
export let months = ['Jan', 'Feb', 'Mar','Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
// 导出 const 声明的变量
export const MODULES_BECAME_STANDARD_YEAR = 2015;
// 导出类
export class User {
constructor(name) {
this.name = name;
}
}
//导出函数
export function sayHi(user) {
alert(`Hello, ${user}!`);
}
导出和声明分开
export {sayHi, sayBye}; // 导出变量列表
Export “as”
可以使用 as
让导出具有不同的名字。
export {sayHi as hi, sayBye as bye};
import导入
写法:
import {sayHi, sayBye} from './say.js';
import * as say from './say.js';
示例:
我们可以把要导入的东西列在花括号 import {...}
中
如果有很多要导入的内容,我们可以使用 import * as <obj>
将所有内容导入为一个对象
推荐使用方法一,明确列出需要导入的内容
Import “as”
可以使用 as
让导入具有不同的名字。
Export default
模块提供了一个特殊的默认导出 export default
语法,以使“一个模块只做一件事”
将 export default
放在要导出的实体前:
这种方式的导出,它的导入不需要花括号
注:
-
可以在一个模块中同时有默认的导出和命名的导出,但是实际上通常不会混合使用它们。
-
模块要么是命名的导出要么是默认的导出。
-
由于每个文件最多只能有一个默认的导出,因此导出的实体可能没有名称。
-
import
命名的导出时需要花括号, -
import
默认的导出时不需要花括号。
如果没有default下面这些就会报错
CommonJS VS ESM
*可以混用,但是不建议( import commonjs || import中require)
其他模块规范
AMD是RequireJS在推广过程中规范化产出,异步加载,推崇依赖前置;
CMD是SeaJS在推广过程中规范化产出,异步加载,推崇就近依赖;
UMD (Universal Module Definition)规范,兼容AMD和CommonJS模式
结语
参考:
《The Modern JavaScript Tutorial》
如以上有错误的地方,请在评论区中指出!
如果有收获的话,就留个赞鼓励一下吧!🍜