细说JS模块化规范(CommonJS、AMD、CMD、ES6 Module)

这是我参与8月更文挑战的第8天,活动详情查看:8月更文挑战前端


模块化

模块化及其优势

模块化就是将系统分离成独立功能的模块,这样咱们须要什么功能,就加载什么功能。web

模块化的优势编程

  • 避免命名空间的冲突(减小命名空间的污染)
  • 更好的分离,实现按需加载
  • 提升可代码的复用性
  • 提升了代码的维护性

JS模块化大体发展过程

CommonJS(服务端) =>AMD(浏览器) => CMD => ES6 Module模块化浏览器

模块化规范的种类

image.png

模块化规范的发展趋势

image.png 前端模块规范有三种:CommonJs,AMD和CMD。
CommonJs用在服务器端,AMD和CMD用在浏览器环境
AMD 是 RequireJS 在推广过程当中对模块定义的规范化产出。
CMD 是 SeaJS 在推广过程当中对模块定义的规范化产出。\缓存

AMD:提早执行(异步加载:依赖先执行)+延迟执行
CMD:延迟执行(运行到需加载,根据顺序执行)服务器

CommonJS 规范

CommonJS 的概念

Node 应用由模块组成,采用 CommonJS 模块规范。markdown

每一个文件就是一个独立模块有本身的做用域。在一个文件里面定义的变量、函数、类,都是私有的,对其余文件不可见。网络

  • 每一个文件均可以做为一个模块(这里的文件指的是js文件)
  • 在服务器端:模块的加载是运行时同步加载的
  • 在浏览去端:模块须要提早编译打包处理,否则浏览器不能识别require语法

CommonJS 的使用

主要分为定义模块和引入模块两个步骤。异步

定义模块模块化

CommonJS规范规定,每一个模块内部,module变量表明当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,实际上是加载该模块的module.exports属性。

// 经过`module.exports`输出变量`x`和函数`addX`
let x = 1;
let addX = function (value) {
  return value + x;
};
module.exports.x = x;
module.exports.addX = addX;
复制代码

引入模块

require方法用于加载模块。

var example = require('./example.js');
复制代码

CommonJS 的特色

  • 全部代码都运行在模块做用域,不会污染全局做用域。
  • 模块能够屡次加载,可是只会在第一次加载时运行一次,而后运行结果就被缓存了,之后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
  • 模块加载是一项阻塞操做,也就是同步加载。模块加载的顺序,按照其在代码中出现的顺序。

模块的加载机制

CommonJS模块的加载机制是,输入的是被输出的值的拷贝。也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。

AMD 规范

ADM 概念

AMD规范全称是Asynchronous Module Definition,即异步模块加载机制,主要用于浏览器。它完整描述了模块的定义,依赖关系,引用关系以及加载机制。因为该规范不是原生js支持的,使用AMD规范进行开发的时候须要引入第三方的库函数,AMD对应的就是鼎鼎大名的RequireJS

  • CommonJS规范出现后,在Node开发中产生了很是好的效果,开发者但愿借鉴这个经验来解决浏览器JS的模块化
  • 可是大部分人认为浏览器和服务器的环境差异太大,毕竟浏览器JS是经过网络动态以此加载的,而服务器的JS是保存在本地磁盘中。所以浏览器须要实现异步加载,模块在定义的时候就必须先知名它所须要依赖的模块,而后把本模块的代码写在回调函数中执行,最终衍生出了AMD规范
  • AMD的主要思想时异步模块,主逻辑在函数回调中执行。

AMD 的使用

define和require这两个定义模块、调用模块的方法,合称为AMD模式。它的模块定义的方法很是清晰,不会污染全局环境,可以清楚地显示依赖关系。

使用define用来暴露模块,使用require用来引入模块;require()异步加载模块,浏览器不会失去响应;它指定的回调函数,只有前面的模块都加载成功后,才会运行,解决了依赖性的问题。

AMD模式能够用于浏览器环境,而且容许异步加载模块,也能够根据须要动态加载模块。

define方法:定义模块

define方法用于定义模块,RequireJS要求每一个模块放在一个单独的文件里。 按照是否依赖其余模块,能够分红两种状况讨论。第一种状况是定义独立模块,即所定义的模块不依赖其余模块;第二种状况是定义非独立模块,即所定义的模块依赖于其余模块。

require方法:调用模块

require方法用于调用模块。它的参数与define方法相似。

RequireJS主要解决了两个问题:

  1. 多个js文件可能有依赖关系,被依赖的文件须要早于依赖它的文件加载到浏览器
  2. js加载的时候浏览器会中止页面渲染,加载文件越多,页面失去响应时间越长

AMD规范与CommonJS规范的兼容性

CommonJS规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操做。AMD规范则是非同步加载模块,容许指定回调函数。因为Node.js主要用于服务器编程,模块文件通常都已经存在于本地硬盘,因此加载起来比较快,不用考虑非同步加载的方式,因此CommonJS规范比较适用。可是,若是是浏览器环境,要从服务器端加载模块,这时就必须采用非同步模式,所以浏览器端通常采用AMD规范。

CMD 规范

CMD 的概念

CMD(Common Module Definition) ,通用模块定义,它解决的问题和AMD规范是同样的,只不过在模块定义方式和模块加载时机上不一样,CMD也须要额外的引入第三方的库文件,SeaJS,SeaJS推崇一个模块一个文件

  • AMD/RequireJS的JS模块实现有不少不优雅的地方,主要缘由不能以一种更好的管理模块的依赖加载和执行;
  • 那么就出现了SeaJS,SeaJs遵循的是CMD规范,CMD规范在AMD的基础上改进的一种规范,解决了AMD对依赖模块的执行时机的问题;
  • SeaJS模块化的顺序是:模块化预加载=》主逻辑调用模块时才执行模块中的代码;
  • SeaJS的用法和AMD基本相同,而且融合了CommonJS的写法;

CMD 的使用

(对于模块的引入,具备同步和异步两中方式)

define 是一个全局函数,用来定义模块

SeaJS提供了seajs.use来加载模块

SeaJS的出现,是CommonJS在浏览器的践行者,并吸取了RequireJS的优势

ES6中的Module模块

仅支持静态导入导出

ES6 规范只支持静态的导入和导出,ES Module 须要在编译时期进行模块静态优化,也就是必需要在编译时就能肯定。

为何要这么作,主要是两点:

  1. 性能,在编译阶段即完成全部模块导入,若是在运行时进行会下降速度
  2. 更好的检查错误,好比对变量类型进行检查

ES6 Module 使用

  • 模块功能主要由两个命令构成:exportimport
  • export用于暴露接口,import用于引入模块

在使用 ES Module 值得注意的是:import 和 export 命令只能在模块的顶层,在代码块中将会报错,这是由于 ES Module 须要在编译时期进行模块静态优化,import 和 export 命令会被 JavaScript 引擎静态分析,先于模块内的其余语句执行,这种设计有利于编译器提升效率,但也致使没法在运行时加载模块(动态加载)。

对于这个缺点,TC39 有了一个新的提案 – Dynamic Import,提案的内容是建议引入 import()方法,实现模块动态加载。

// specifier: 指定所要加载的模块的位置
import(specifier)
复制代码
  • import()函数能够用在任何地方,不只仅是模块,非模块的脚本也可使用。

它是运行时执行,也就是说,何时运行到这句话,就会加载到指定的模块。另外,import()函数所加载的模块没有静态连接关系,这点也是与import语法不一样

  • 注意的时ES6 的Module语法有些浏览器是不支持的,所以须要Babel先进性转码,将import和export命令转成ES5语法才能被浏览器解析。

CommonJS、AMD、CMD、ES6 Module的区别

AMD与CMD区别

  1. 模块定义时对依赖的处理不一样

    AMD推崇前置依赖,在定义模块时就要声明其依赖的模块;而CMD推从就近依赖,只有在用到某个模块时再使用require导入;

  2. 对依赖模块的处理机制不一样

    首先AMD和CMD对模块的加载方式都是异步的 不过区别在于AMD当加载了依赖模块以后当即执行依赖模块,依赖模块的执行顺序和咱们书写的顺序不必定一致; 而CMD加载完依赖模块以后,并不会当即执行,等全部的依赖模块都加载好以后,进入回到函数逻辑,遇到require语句的时候,才执行对应的模块,这样模块的执行顺序就和咱们书写的时候一致了

ES6模块与CommonJS模块加载的区别

CommonJS时运行时加载,由于ComminJS加载是先加载整个模块,生成一个对象(这个对象包含了path这个模块的全部API),而后再从这个对象上面读取方法-----运行时加载 ES6是编译时加载ES6模块不是对象,它的对外接口只是一种静态定义,在代码静态定义阶段就会生成-----编译时加载

//ES6模块
import { basename, dirname, parse } from 'path';

//CommonJS模块
let { basename, dirname, parse } = require('path');
复制代码

以上这种写法与CommonJS的模块加载有什么不一样?

  • 当require path 时,CommonJS会将path模块运行一遍,并返回一个对象,这个对象包含path模块的全部API。

总结

模块化就是将系统分离成独立功能的模块,这样咱们须要什么功能,就加载什么功能。

模块化的优势

  • 避免命名空间的冲突(减小命名空间的污染)
  • 更好的分离,实现按需加载
  • 提升可代码的复用性
  • 提升了代码的维护性

JS模块化发展产物:CommonJS(服务端) =>AMD(浏览器) => CMD => ES6 Module模块化

CommonJs主要用在服务器端,AMD和CMD用在浏览器环境 AMD 是 RequireJS 在推广过程当中对模块定义的规范化产出。 CMD 是 SeaJS 在推广过程当中对模块定义的规范化产出。 ES6 规范只支持静态的导入和导出,与CommonJS 的运行时加载不一样的是ES6是编译时加载,ES Module 是在编译时期进行模块静态优化。


若是这篇文章帮到了你,记得点赞👍收藏加关注哦😊,但愿点赞多多多多...

文中若有错误,欢迎在评论区指正

相关文章
相关标签/搜索