一、模块化演变过程
Stage1 文件划分方式
- 污染全局作用域
- 命名冲突问题
- 无法管理模块依赖
- 早起模块化完全依靠约定
Stage2 命名空间方式
- 每个模块只暴露一个全局对象,所有模块都挂载到这个对象上
- 减少了命名冲突的可能
- 但是没有私有空间,模块成员可以在外部被访问或修改
- 模块之间的依赖关系没有得到解决
Stage3 IIFE 立即执行函数
- 使用立即执行函数包裹代码,要输出的遍历挂载到一个全局对象上
- 变量拥有了私有空间,只有通过闭包修改和访问变量
- 参数作为依赖声明去使用,使得每个模块的依赖关系变得明显
二、模块化规范
1. CommonJS规范
nodejs的规范
- 一个文件就是一个模块
- 每个模块都有单独的作用域
- 通过module.exports导出成员
- 通过require函数载入模块
- CommonJS是以同步模式加载模块
node的机制是启动运行时加载模块,执行过程中只使用,在浏览器环境中会导致效率低,每次页面加载会导致大量同步请求,所以早期是使用AMD而不是commonJS
commonJK模块输出的值是值的拷贝
2. AMD(Asynchronous Module Definition)异步模块规范
模块加载器:Require.js
// 定义一个模块 模块名 声明依赖项 函数(参数和依赖项对应)
define('module1', ['jquery', './module2'], function ($, module2) {
return {
start: function () {
$('body').animate({ margin: '200px' })
module2()
}
}
})
// 载入一个模块
require(['./module1'], function (module1) {
module1.start()
})
加载时会自动创建一个script标签,发送对应脚本文件的请求,并执行相应的模块代码
- 目前绝大多数第三方库都支持AMD规范
- AMD使用起来相对复杂
- 模块JS文件请求频繁
3.淘宝推出的Sea.js + CMD(Common Module Definition)通用模块规范
// CMD 规范 (类似 CommonJS 规范)
define(function (require, exports, module) {
// 通过 require 引入依赖
var $ = require('jquery')
// 通过 exports 或者 module.exports 对外暴露成员
module.exports = function () {
console.log('module 2~')
$('body').append('<p>module2</p>')
}
})
4. ES Module
特性
// ./modulejs
const foo = 'es modules'
export { foo }
//export { foo as ddd }
// ./app.js
import { foo } from './module.js'
//import { ddd } from './module.js'
console.log(foo) // => es modules
- 自动采用严格模式,忽略’use strict’
- 每个ESM模块都是单独的私有作用域
- ESM是通过CORS去请求外部JS模块的
- ESM的script标签会延迟执行脚本
导出
- 导出的语法不是ES6的对象字面量对象
- 导出的是成员的引用,且只读 ```javascript var name = ‘jack’ var age = 22 export {name, age}
import {name, age} from ‘./moddle.js’ console.log(name,age)
export default {name, age} 这才是导出对象
- 导出的时候如果将导出成员的名称设置为default,在导入的时候就必须重命名
```javascript
export { foo as default }
import { default as foo } from './module.js'
----------------------------
let name = 'module'
export default name
import name from './module.js' //默认导出的特殊用法
导入
- 导入不是解构,这是固定语法
- 导入时,from后续跟的字符串必须是完整的文件名,相对路径不能省略 ./ ,另外可以直接引用cdn路径的模块
导入时
- 如果只需要执行文件,则可以保持花括弧内为空,或者直接import ‘xx’
- 可以用*代替全部成员
import {} from './module.js' import './module.js' import * as mod from './module.js'
动态路径使用import方法。返回promise,then的参数是模块,可以访问变量 ```javascript // var modulePath = ‘./module.js’ // import { name } from modulePath // console.log(name)
// if (true) { // import { name } from ‘./module.js’ // }
import(‘./module.js’).then(function (module) { console.log(module) })
- default和多个导出同时存在时
```javascript
import { name, age, default as abc} from './module.js'
import abc, { name, age } from './module.js'
//两种都可以
console.log(name, age, abc)
- 当有多个零散模块时,可以集中到index.js中导出 ```javascript import {Button} from ‘./components/button.js’ import {Avatar} from ‘./components/Avatar.js’ export {Button,Avatar}
export {Button} from ‘./components/button.js’ export {Avatar} from ‘./components/Avatar.js’ //假如button.js是默认导出export default的话,要修改写法 export { default as Button} from ‘./components/button.js’
<a name="BAzbM"></a>
### 兼容
开发阶段
```javascript
<script nomodule src="https://unpkg.com/promise-polyfill@8.1.3/dist/polyfill.min.js"></script>
<script nomodule src="https://unpkg.com/browser-es-module-loader@0.4.1/dist/babel-browser-build.js"></script>
<script nomodule src="https://unpkg.com/browser-es-module-loader@0.4.1/dist/browser-es-module-loader.js"></script>
<script type="module">
import { foo } from './module.js'
console.log(foo)
</script>
5. 模块化标准规范
- 在node.js中使用CommonJS
CommonJS是node.js内置的模块化工具,只需要遵循CommonJS的标准即可,不需要引入别的依赖 - 在浏览器中使用ES Modules
ES Modules是ECMAScript2015npm config set registry https://registry.npmmirror.com