模块化开发时当前最重要的前端开发范式之一 模块化只是思想

一、模块化演变过程

Stage1 文件划分方式

  • 污染全局作用域
  • 命名冲突问题
  • 无法管理模块依赖
  • 早起模块化完全依靠约定

Stage2 命名空间方式

  • 每个模块只暴露一个全局对象,所有模块都挂载到这个对象上
  • 减少了命名冲突的可能
  • 但是没有私有空间,模块成员可以在外部被访问或修改
  • 模块之间的依赖关系没有得到解决

Stage3 IIFE 立即执行函数

  • 使用立即执行函数包裹代码,要输出的遍历挂载到一个全局对象上
  • 变量拥有了私有空间,只有通过闭包修改和访问变量
  • 参数作为依赖声明去使用,使得每个模块的依赖关系变得明显

二、模块化规范

1. CommonJS规范

nodejs的规范

  • 一个文件就是一个模块
  • 每个模块都有单独的作用域
  • 通过module.exports导出成员
  • 通过require函数载入模块
  • CommonJS是以同步模式加载模块

node的机制是启动运行时加载模块,执行过程中只使用,在浏览器环境中会导致效率低,每次页面加载会导致大量同步请求,所以早期是使用AMD而不是commonJS
commonJK模块输出的值是值的拷贝

2. AMD(Asynchronous Module Definition)异步模块规范

模块加载器:Require.js

  1. // 定义一个模块 模块名 声明依赖项 函数(参数和依赖项对应)
  2. define('module1', ['jquery', './module2'], function ($, module2) {
  3. return {
  4. start: function () {
  5. $('body').animate({ margin: '200px' })
  6. module2()
  7. }
  8. }
  9. })
  10. // 载入一个模块
  11. require(['./module1'], function (module1) {
  12. module1.start()
  13. })

加载时会自动创建一个script标签,发送对应脚本文件的请求,并执行相应的模块代码

  • 目前绝大多数第三方库都支持AMD规范
  • AMD使用起来相对复杂
  • 模块JS文件请求频繁

3.淘宝推出的Sea.js + CMD(Common Module Definition)通用模块规范

  1. // CMD 规范 (类似 CommonJS 规范)
  2. define(function (require, exports, module) {
  3. // 通过 require 引入依赖
  4. var $ = require('jquery')
  5. // 通过 exports 或者 module.exports 对外暴露成员
  6. module.exports = function () {
  7. console.log('module 2~')
  8. $('body').append('<p>module2</p>')
  9. }
  10. })

4. ES Module

特性

  1. // ./modulejs
  2. const foo = 'es modules'
  3. export { foo }
  4. //export { foo as ddd }
  5. // ./app.js
  6. import { foo } from './module.js'
  7. //import { ddd } from './module.js'
  8. 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} 这才是导出对象

  1. - 导出的时候如果将导出成员的名称设置为default,在导入的时候就必须重命名
  2. ```javascript
  3. export { foo as default }
  4. import { default as foo } from './module.js'
  5. ----------------------------
  6. let name = 'module'
  7. export default name
  8. 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是ECMAScript2015
    npm config set registry https://registry.npmmirror.com