本文围绕js模块化介绍 commonjs 、 esmodule 和静态加载与动态加载。
1. 模块化
早期 JavaScript 开发很容易存在全局污染和依赖管理混乱问题。模块化就是把各个功能拆解成各个模块。而规范就有 commonjs 和esmodule ,我们把每一个js文件称之为模块。
1.1 避免污染全局变量
没有模块化,那么 script 内部的变量是可以相互污染的。比如有一种场景,有./index.js 文件和 ./list.js 文件为 功能A 开发的,./home.js 为 功能B 开发的。他们都有name变量,就会造成变量污染,可能达不到相应的预期。
1.2 依赖管理
依赖管理也是一个难以处理的问题。正常情况下,执行 js 的先后顺序就是 script 标签排列的前后顺序。那么如何三个 js 之间有依赖关系,那么应该如何处理呢?如果加载三个js未免太过紊乱,那么就需要模块化。
2. Commonjs的基本使用
commonjs 的提出,弥补 Javascript 对于模块化,没有统一标准的缺陷。nodejs 借鉴了 commonjs 的 Module ,实现了良好的模块化管理。
2.1 导出
CommonJs使用 module.exports 导出, 但有 对象 和 属性 两种写法。
// 导出一个对象
module.exports = {
name: "z",
age: 24,
sex: "male"
}
// 导出某个属性
module.exports.name = "z"
module.exports.sex = null
module.exports.age = undefined
2.2 直接导出
导出也可以省略module关键字
exports.name = ""
exports.sex = "male"
2.3 导入
CommonJs中使用require语法可以导入 整个对象,如果想要单个的值,可以通过 解构 对象来获取。
let data = require("./index.js")
console.log(data) // { name: "z", age: 24 }
//index.js
module.exports.name = "z"
module.exports.age = 24
2.4 动态导入
CommonJs支持动态导入,指的就是运行中加载,那么我们使用require语法就有如下写法:
let lists = ["./index.js", "./config.js"]
lists.forEach((url) => require(url)) // 动态导入
if (lists.length) {
require(lists[0]) // 动态导入
}
2.5 导入的值是拷贝的
let { num, add } = require("./index.js")
num = 10//修改对其它引用不造成影响
//index.js
module.exports = {
num:0,
add() {
++ this.num
}
}
3. ES Module
es6提出了ESModule。
3.1 导出
语法上 ESModule 有两种导出,体现在关键字上,单个导出(export)、默认导出(export default)。单个导出在导入时不像CommonJs一样直接把值全部导入进来了,默认导出就是全部直接导入进来,当然Es Module中也可以导出任意类型的值。
// 单个导出
export const name = "z"
export const age = 24
// 默认导出
export default {
fn() {},
msg: "z"
}
3.2 导入
(1)单个导入,使用import…from语法带花括号{} ,注意不是解构只是语法相似。
import { name, age } from './index.js';
import { name as zname, age as zage} from './index.js';//重命名
import * as idx from './index.js';//如果想合并所有单个就这么写
console.log(name, age) // "z" 24
// index.js
export const name = "z"
export const age = 24
(2)默认导入需要重命名
// 匿名导出的,要重命名
import getName from './index.js';
// index.js
const Programmer = {name: 'z',age:24}
export default Programmer
3.3 混合导入 (注意)
//那么先导msg
import msg, { name, age } from './index.js'
console.log(msg) // { msg: "z" }
//如果导出同时用了export default 和 export
export const name = "z"
export const age = 24
export default {
msg: "z"
}
3.4 导入的值是引用地址
export导出的值是值的引用,而且导入的值,不能进行修改也就是只读状态。
import { num, add } from "./index.js"
console.log(num) // 0
add()
console.log(num) // 1
num = 10 // 抛出错误
//index.js
export let num = 0;
export function add() {
++ num
}
3.5 Es Module静态分析
就是Es Module语句 import只能声明在该文件的最顶部,不能动态加载语句,Es Module 语句运行在代码编译时。
if (true) {
import xxx from 'XXX' // 报错
}
4. 缓存
不管是CommonJs还是Es Module都不会重复导入,就是只要该文件内加载过一次这个文件了,再次导入一次是不会生效的。
5. 总结
commonjs是动态加载,同步执行;esmodule是静态分析,动态引入(esmodule也可以动态导入,在es2020增加基于 promise 的 import() 函数)
commonjs的导出方式是 有属性和对象写法的 module.exports(可省略module);esmodule的导入方式是 export 和 export default
commonjs的导入方式是 require();esmodule的导入方式是 import…from;