2-2-1 模块化开发
1) 模块化演变过程
- 基于文件的划分模块的方式
- 污染全局作用域
- 命名冲突问题
- 无法管理模块依赖关系
- 命名空间方式
每个模块只暴露一个全局对象,所有模块成员都挂载到这个对象上
- IIFE 立即执行函数
- 利用 IIFE 参数作为依赖声明使用
- AMD 规范 (require.js)
- AMD 使用起来相对复杂
- 模块JS文件请求频繁
- Sea.js+CMD(淘宝推出)
2) 模块化标准规范
- CommonJs in Node.js
- ES Module in browser
3) ES Moudle 基本特性
- 自动采用严格模式,忽略‘use strict’
- 每个ESM模块都是单独的私有作用域
- ESM是通过CORS去请求外部JS模块的
- ESM的script标签会延迟执行脚本
<!-- 通过给 script 添加 type = module 的属性,就可以以 ES Module 的标准执行其中的 JS 代码了 -->
<script type="module">
console.log('this is es module')
</script>
<!-- 1. ESM 自动采用严格模式,忽略 'use strict' -->
<script type="module">
console.log(this)
</script>
<!-- 2. 每个 ES Module 都是运行在单独的私有作用域中 -->
<script type="module">
var foo = 100
console.log(foo)
</script>
<script type="module">
console.log(foo)
</script>
<!-- 3. ESM 是通过 CORS 的方式请求外部 JS 模块的 -->
<!-- <script type="module" src="https://unpkg.com/jquery@3.4.1/dist/jquery.min.js"></script> -->
<!-- 4. ESM 的 script 标签会延迟执行脚本 -->
<script defer src="demo.js"></script>
<p>需要显示的内容</p>
4) ES Module 导出导入
// 导出
// export var name = 'foo module'
// export function hello () {
// console.log('hello')
// }
// export class Person {}
var name = 'foo module'
function hello () {
console.log('hello')
}
class Person {}
// export { name, hello, Person }
// export {
// // name as default,
// hello as fooHello
// }
// export default name
// var obj = { name, hello, Person }
export { name, hello, Person }
// 导入
// import { default as fooName } from './module.js'
// console.log(fooName)
import { name, hello, Person } from './module.js'
console.log(name, hello, Person)
5) ES Module 浏览器环境 Polyfill(兼容)
6)ES Module in Node.js
- 使用要求node版本8以上
- 文件改为 .mjs 后缀
// 第一,将文件的扩展名由 .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'))
- 与 CommonJs 交互
- CommonJs 模块始终只会导出一个默认成员(相当于ES Module 中的export default)
- import 不是解构导出对象
- ES Module 中可以导入 CommonJs 模块
- CommonJs 中不能导入 ES Module 模块
- 与 CommonJs 差异
// esm.mjs
// ESM 中没有模块全局成员了
// // 加载模块函数
// console.log(require)
// // 模块对象
// console.log(module)
// // 导出对象别名
// console.log(exports)
// // 当前文件的绝对路径
// console.log(__filename)
// // 当前文件所在目录
// console.log(__dirname)
// -------------
// require, module, exports 自然是通过 import 和 export 代替
// __filename 和 __dirname 通过 import 对象的 meta 属性获取
// const currentUrl = import.meta.url
// console.log(currentUrl)
// 通过 url 模块的 fileURLToPath 方法转换为路径
import { fileURLToPath } from 'url'
import { dirname } from 'path'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
console.log(__filename)
console.log(__dirname)
// cjs. js
// 加载模块函数
console.log(require)
// 模块对象
console.log(module)
// 导出对象别名
console.log(exports)
// 当前文件的绝对路径
console.log(__filename)
// 当前文件所在目录
console.log(__dirname)