模块化最重要的前端开发范式
模块化的演变过程
-
文件划分方式
- 污染全局作用域
- 命名冲突问题
- 无法管理模块依赖关系
- 完全依靠约定
-
命名空间 (可以减小命名冲突的可能)
- 但是依然没有私有空间
- 各个模块的内容依然可以在全局或者模块之外被修改
-
IIFE (函数作用域)
- 依赖声明
- 模块私有化
为了可以减少人为因素带来的隐患以及自动按实际需求去加载相关模块
我们就需要 模块化标准 + 模块加载器
- CommonJS 规范 (node.js)
- 一个文件就是一个模块
- 每个模块都有单独的作用域
- 通过
module.exports导出成员 - 通过
require函数载入模块 - 以同步模式加载模块 (启动的时候加载模块;执行时不加载模块,只会使用)
- AMD
Require.js实现了该规范- 目前绝大多数第三方库都支持
AMD规范 AMD使用起来相当复杂- 模块 JS 文件请求频繁
// 定义一个模块
define('module', ['jquery', './module2'], function ($< module2) {
return {
start: function () {
$('body').animate({ margin: '200px' })
module2()
}
}
})
// 载入一个模块
require(['./module1'], function (module1) {
module1.start()
})
-
Sea.js + CMD
-
模块化标准规范
- CommonJS (node.js)
- ES Modules (浏览器)
-
特性
ESM自动采用严格模式- 每个
ESM模块都是单独的私有作用域 ESM是通过CORS去请求外部 JS 模块的ESM的script标签会延迟执行脚本ESM中可以导入CommonJS模块CommonJS中不能导入ESM模块CommonJS始终只会导出一个默认成员- 注意
import不是解构导出对象
ESM IN NODE
// 第一,将文件的扩展名由 .js 改为 .mjs;
// 第二,启动时需要额外添加 `--experimental-modules` 参数;
import { foo, bar } from './module.mjs'
console.log(foo, bar)
// 此时我们也可以通过 esm 加载内置模块了
import fs from 'fs'
fs.writeFileSync('./foo.txt', 'es module working')
// 也可以直接提取模块内的成员,内置模块兼容了 ESM 的提取成员方式
import { writeFileSync } from 'fs'
writeFileSync('./bar.txt', 'es module working')
// 对于第三方的 NPM 模块也可以通过 esm 加载
import _ from 'lodash'
_.camelCase('ES Module')
// 不支持,因为第三方模块都是导出默认成员
// import { camelCase } from 'lodash'
// console.log(camelCase('ES Module'))
ESM WITH COMMONJS
// ES Module 中可以导入 CommonJS 模块
import mod from './commonjs.js'
console.log(mod)
// 不能直接提取成员,注意 import 不是解构导出对象
import { foo } from './commonjs.js'
console.log(foo)
export const foo = 'es module export value'
// CommonJS 模块始终只会导出一个默认成员
module.exports = {
foo: 'commonjs exports value'
}
exports.foo = 'commonjs exports value'
// 不能在 CommonJS 模块中通过 require 载入 ES Module
const mod = require('./es-module.mjs')
console.log(mod)