前端模块化(AMD-CMD-CommonJS-ESmodule)的区别和使用

173 阅读5分钟

模块化

定义:

将程序划分为一个个小的结构体 有自己的代码逻辑 也会有自己的作用域。在es6之前 都是用AMD CMD CommonJS(支持node) 直到es6出现后 有了ESmodule

在没有模块化解析的时候 虽然利用了函数本身自带的作用域并且使用了闭包来区分各个模块 但是这种办法会有缺陷

  1. 函数名有可能会重复
  2. 命名不好管理
  3. 要记得每一个模块的名字

AMD 应用于浏览器的一种模块化规范 异步加载

1.概念

  • AMD是Asynchronous Module Definition(异步模块定义)的缩写
  • 它采用的是异步加载模块
  • 事实上AMD规范要早于CommonJS 但是CommonJS目前依旧被使用(基本上node环境) AMD已经使用的很少了
  • AMD实现基于require.js库和curl.js库 这两个库git hub作者已经有2-3年没有更新过代码了 2.使用
  • 引入require.js
  • data-main属性的作用是在加载完src的文件后会加载执行该文件

CMD 应用于浏览器的一种模块化规范 异步加载

1.概念

  • CMD是Common Module Definition(通用模块定义)的缩写
  • 他也采用了异步加载模块 但是它将CommonJS的优点都吸收了过来
  • 目前CMD也很少有人使用了
  • 他基于SeaJS库 2.使用
  • 引入SeaJS
  • seaJS是指定主入口

CommonJS 规范 同步加载

表现形式

  1. Node是CommonJS在服务端具有代表的实现
  2. Browserify是CommonJS在浏览器中一种实现
  3. webpack 打包工具对CommonJS的支持和转换 导出和导入 exports 和 module.exports都是负责对模块进行导出 require函数可以帮助我们导入其他模块(自定义模块,系统模块,第三方库模块)中内容 在node中 读取node核心模块 const fs = require('fs') 寻找过程 const nmu=require(./abc)
  4. 如果有后缀名: 他会找对应文件下自动去找后缀名为index.js的文件
  5. 如果没有后缀名: 他会先去寻找abc文件 再去查找abc.js文件 再去寻找abc.json文件 最后找abc.node文件
  6. 如果不是核心业务模块:他会依次在每一层的node modules 寻找你引入的模块
  7. 如果都没找到 他会报 not found 模块的加载过程
  • 在模块被第一次引用的时候 模块化中js会被运行一次(因为他得看模块代码中是否还有其他模块的引用一次寻找)
  • 在第一次运行完的时候 他会被加入缓存里面 这样以后有其他地方导入 可以直接走缓存,在node源码每一个js都有一个modules实例 在实例里面记录了一个变量loading 当第一次加载完毕 这个变量会变成true
  • 当模块互相被引用的时候 就会形成一个图结构 在图结构遍历的过程中 他有两种方式 1.深度优先搜索算法(DFS)会优先一条路径走到底 2.广度优先搜索算法(BFS)会发散引用 从各个方向去引用 CommonJS 不适合在浏览器中 因为浏览器需要先从服务器中将导入的文件下载下来 之后再加载运行比较适合node端 也可以使用browserify 这个异步加载 exports和module.exports的区别 exports.name='1234' module.exports={name:'123123'} 在底层实现上 module.exports={} 在最后他会有 exports=module.exports这个步骤所以 他俩用法都相等 但是这种事错误的写法 exports={name:'213'} 因为他把exports指向了一个新的对象 而不是原来的module.exoports了

ES Module 异步加载

定义:

  • 它使用了import和export
  • 它采用了编译期的静态分析 并加入了动态引入
  • 如果脱离了webpack的编译的话 需要在html引入的 正常情况下哎单纯的解析js 他是不是别import关键字的 所以需要在标签上加上type=‘module’

具体使用

export
1.export 跟上声明语句
export const name =12

2.export 跟上导出
export{
name,
age
}

3.导出的时候起别名
export{
name as fname,
age as fage
}

4.默认导出
export default foo

export{
name as default,
}
import

1.普通导入
import {name} from './模块化.js'

2.起别名
import {name as fname,age as fage} from './模块化.js'

3.导出的所有内容放到一个标识符

import \* as foo from './模块化.js'

4.默认导出

import why from './模块化.js'

import 函数 他不会堵塞后续代码执行(异步) 他返回的是一个 promise
import("./模块化.js").then(res=>{
console.log(res)
})
import.meta 这是ES11新增的属性 获取你当前文件的下载路径

ESmodule的加载流程

  • 阶段一:构建阶段 查找到服务器中的js文件地址 并且下载浏览器里面的文件 将其解析成模块记录吧(数据结构) 他是一个静态分析过程 做好不要在逻辑代码里面使用 他会生成一个module record的数据结构 来解析是否继续依赖其他文件,当一个文件被多处依赖的时候 他不会进行多次下载 当他在第一次加载下载的的时候 在浏览器会进行一个map的映射关系进行缓存 请求一次后会加入里面

  • 阶段二:实例化 对模块记录进行实例化,并且分配内存空间,解析模块的导入和导出语句 把模块指向对应的内存地址 但是内部值都是undefined 还没有赋值

  • 阶段三:运行 运行代码 填入值 并且将值填充到内存地址中

只有导出的地方可以修改这个值 但是在导入的地方无法修改值 CommonJs和es module相互调用 1.在浏览器中 他俩不可以互相调用 在浏览器里面默认不支持commonJs的 他会报未定义common的 2.在node中需要区分node版本 在老版本是不支持es module的 需要加上ejs的 在新版本还是慢慢开始支持es module 3. 平时开发(基于webpack) 他是完全支持的