解决问题
- 全局变量污染
- 依赖混乱
- 模块化出现后,我们就可以把臃肿的代码细分到各个小文件中,便于后期维护管理
前端主要有两大模块化标准:
- CommonJS,简称CMJ,这是一个社区规范,出现时间较早,目前仅node环境支持
- ES Module,简称ESM,这是随着ES6发布的官方模块化标准,目前浏览器和新版本node环境均支持
CommonJS
node天生支持CommonJS模块化标准
node规定:
-
node中的每个js文件都是一个CMJ模块,通过node命令运行的模块,叫做入口模块
-
模块中的所有全局定义的变量、函数,都不会污染到其他模块
-
模块可以暴露(导出)一些内容给其他模块使用,需要暴露什么内容,就在模块中给
module.exports赋值 -
一个模块可以导入其他模块,使用函数
require("要导入的模块路径")即可完成,该函数返回目标模块的导出结果- 导入模块时,可以省略
.js - 导入模块时,必须以
./或../开头
- 导入模块时,可以省略
-
一个模块在被导入时会运行一次,然后它的导出结果会被node缓存起来,后续对该模块导入时,不会重新运行,直接使用缓存结果
小案例 动态打印
模块划分
- main.js 入口模块
- delay.js 延时模块
- print.js 打印模块
- config 数据模块
运行
node main.js
main.js
const delay = require("./delay.js");
const print = require("./print.js");
const config = require("./config.js");
/**
* 运行该函数,会逐字打印config.js中的文本
* 每个字之间的间隔在config.js已有配置
*/
async function run() {
for (let i = 0; i < config.text.length; i++) {
print(i);
await delay(config.wordDuration);
console.clear()
}
}
run()
delay.js
/**
* 该函数返回一个Promise,它会等待指定的毫秒数,时间到达后该函数完成
* @param {number} ms 毫秒数
* @returns {Promise}
*/
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
module.exports = delay;
print.js
const config = require("./config.js");
/**
* 该函数会做以下两件事:
* 1. console.clear() 清空控制台
* 2. 读取config.js中的text配置,打印开始位置到index位置的字符
* @param {number} index
*/
function print(index) {
console.clear();
console.log(config.text.slice(0, index));
}
module.exports = print;
config.js
module.exports = {
wordDuration: 100, // 打印每个字的时间间隔
text: `西风烈,
长空雁叫霜晨月。
霜晨月,
马蹄声碎,
喇叭声咽。
雄关漫道真如铁,
而今迈步从头越。
从头越,
苍山如海,
残阳如血。`, // 要打印的文字
};
ES Module
ES Module的导出
ES Module分为两种导出方式:
- 具名导出(普通导出),可以导出多个
- 默认导出,只能导出一个
一个模块可以同时存在两种导出方式,最终会合并为一个「对象」导出
导出
export const a = 1; // 具名,常用
export function b() {} // 具名,常用
export const c = () => {} // 具名,常用
const d = 2;
export { d } // 具名
const k = 10
export { k as temp } // 具名
// export default 3 // 默认,常用
// export default function() {} // 默认,常用
// const e = 4;
// export { e as default } // 默认
const f = 4, g = 5, h = 6
export { f, g, h as default} // 基本 + 默认
// 以上代码将导出下面的对象
/*
{
a: 1,
b: fn,
c: fn,
d: 2,
temp: 10,
f: 4,
g: 5,
default: 6
}
*/
注意:导出代码必须为顶级代码,即不可放到代码块中
导入
针对具名导出和默认导出,有不同的导入语法
// 仅运行一次该模块,不导入任何内容
import "模块路径"
// 常用,导入属性 a、b,放到变量a、b中。a->a, b->b
import { a, b } from "模块路径"
// 常用,导入属性 default,放入变量c中。default->c
import c from "模块路径"
// 常用,default->c,a->a, b->b
import c, { a, b } from "模块路径"
// 常用,将模块对象放入到变量obj中
import * as obj from "模块路径"
// 导入属性a、b,放到变量temp1、temp2 中
import {a as temp1, b as temp2} from "模块路径"
// 导入属性default,放入变量a中,default是关键字,不能作为变量名,必须定义别名
import {default as a} from "模块路径"
//导入属性default、b,放入变量a、b中
import {default as a, b} from "模块路径"
// 以上均为静态导入
import("模块路径") // 动态导入,返回一个Promise,完成时的数据为模块对象
注意:静态导入的代码必须为在代码顶端,也不可放入代码块中
另外,静态导入的代码绑定的符号是常量,不可更改