常见模块开发规范(CommonJS、AMD、CMD 以及 ES6 模块加载)详解

模块化开发

模块化开发是指在程序开发中,可以将每个功能脚本或文件单独提炼出来作为一个个独立的模块,模块之间相互关联,相互依赖,并对外暴露出一个入口文件。既方便开发者自己引用,也方便他人下载使用。比如前端经常用到的 Ant Design 组件,就可以看作是一个模块,在 Ant Design 中也按功能分了许多子模块,像轮播图组件、日期选择组件、导航组件等,模块化开发更便于使用和维护。

前端(浏览器)为什么要使用模块化开发

在传统的 HTML 应用中,脚本加载通过 script 标签的 src 属性,有几个脚本就引用几个资源。



这样加载资源,有几个缺陷;

  • 脚本的加载会阻塞 DOM 树的渲染
  • 发起多次请求
  • 脚本与脚本引用之间不方便
  • 如果脚本之间相互依赖,就必须按照顺序加载,不利于维护
  • 基于以上原因,不适合大型应用

script 标签提供了 defer 属性 和 async 属性来实现异步加载,不去阻塞 DOM 渲染。

  • async 异步加载,加载的时候与 DOM 渲染并行,但是加载完成之后就开始执行,会中断 DOM 渲染,另外如果有多个 async 脚本,不能保证加载顺序。
  • defer 延时加载,当 DOM 渲染完成以及其他脚本执行完成后,才会执行本脚本;如果有多个 defer 脚本,按序加载。但是 IE 浏览器不支持此属性,所以在应用的时候最好把两个属性都加上。

虽然  script 标签提供了方式实现异步加载,不阻塞 DOM 渲染,但是其他的问题还是没有解决,而模块化开发就能有效的解决以上问题,常见的模块加载器有 CommonJS、CMD、AMD 以及 ES6 模块加载,本文将探讨每个模块加载器的概念、特点、使用方式以及适用场景。

一、CommonJS

1. 概念

由于 JavaScript 没有模块体系和相应的包管理工具,所以不具备大型应用开发能力,因此 CommonJS 应运而生,而用 JavaScript 开发的服务端语言 NodeJS 就实现了 CommonJS 的规范。

2. 特点

  • 一个文件就是一个模块,具备独立的作用域,不会污染全局。
  • 依赖之间同步加载、执行,第一次加载的时候就全部执行了,并且缓存下来,后面执行直接使用缓存。
  • __dirname代表当前模块文件所在的文件夹路径。
  • __filename代表当前模块文件所在的文件夹路径+文件名。

3. 模块使用方式

  • require 加载模块。
  • module.exports 对外暴露接口。

4. 举例

module.exports = path; //对外暴露接口
const path = require('path');//加载模块

5. 应用场景

CommonJS 是代码同步加载,因为服务端的模块都是放在硬盘,加载的时间就是硬盘的读取时间。所以适用于服务端,并不适用于浏览器。

二、AMD(Asynchronous Module Definition)

1. 概念

上文说到 CommonJS 是同步加载,如果前面的程序加载时间太长,后面的程序要一直等待,在浏览器就会长时间出现空白现象,这对于现在越来越丰富的 Web 应用是无法忍受的,为了解决这个问题,AMD 规范就出现了,Require.js 是 AMD 规范的实现。

2. 特点

  • 异步并行加载,不阻塞 DOM 渲染。
  • 提前执行(预执行),在模块使用之前就已经执行完毕。

3. 模块使用方式

  • define(callback),该函数接收一个回调函数,用来定义模块。
  • require([module], callback),该函数接收两个参数,一个是要加载的模块名称数组,一个是回调函数,用来加载模块。
  • requirejs.config(obj),用来对 Require.js 进行配置。

4. 举例

需要引入 require.js,data-main 属性为统一的入口资源文件路径,在 require.js 加载之后加载。

// 定义模块index.js
define(['index'], function(){
    function person(){
        console.log('jack');
    }
    return {
        person: person
    };
});
// 加载模块
require(['index'], function (module){
  module.person();
});     

5. 应用场景

可以在浏览器使用,也可以在 Rhino / Node 等环境使用。

三、CMD(Common Module Definition)

1. 概念

CMD 是通用模块加载,Sea.js 是 CMD 的规范实现,CMD 规范要解决的问题与 AMD 一样,只不过是对依赖模块的执行时机不同 ,CMD 是模块需要用到的时候再去执行,推崇就近依赖。

2. 特点

  • 异步并行加载,也不会阻塞 DOM 渲染。
  • 按需执行(需要的时候才执行)。

3. 模块使用方式

  • require,加载模块。
  • seajs.config(obj),用来对 Sea.js 进行配置。
  • seajs.use(callback),用来在页面中加载一个或多个模块。
  • define(callback),用来定义模块。
  • require.async,用来在模块内部异步加载一个或多个模块。
  • module.exports 与 exports 类似,都是对外提供接口 。

4. 举例

// 定义模块  a.js
define(function(require, exports, module) {
  //引入模块
  var $ = require('jquery.js')
  //对外提供 price 属性  
  exports.price= 200;  
});
// 加载模块
seajs.use(['a'],function(a){
   console.log(a.price)//200
}

5. 应用场景

专注于 Web 浏览器

四、ES6 模块加载

1. 概念

ES6 也可以实现浏览器的模块化加载,需要在 script 标签中加入 “type=module” 属性,浏览器看到这个属性就知道这是个 ES6模块,会等到 DOM 渲染完成,再执行该模块,如果有多个 “type=module” ,浏览器会按序加载,相当于添加了 defer 属性。

2. 特点

在 ES6 中模块化加载需要注意的几点:

  • 默认严格模式(严格模式的顶层 this 指向 undefined,而不是 windows);
  • js后缀不可省略;
  • 变量只在本模块作用域内有效,也就是局部变量;
  • 同一个模块加载多次,只执行一次。

3. 模块化使用方式

  • import ,加载模块
  • export default (指定模块默认输出)向外暴露接口,如果没有 default,引用的时候需要带上{}
//1.使用export default
//main.js
import index from './index.js';
index();
//index.js
export default function index(){
   console.log(1)
}
//2.使用export
//main.js
import {index} from './index.js';
index();
//index.js
export function index(){
   console.log(1)
}

4. 举例

//在网页中引入模块


//脚本与脚本之间的引用与导出
//requireTest.js
import index from './requireIndex.js';
export default function main() {
    index();
}
//requireIndex.js
export default function index(){
    console.log(3333);
}

5. 应用场景

适用于浏览器

五、各模块加载器区别

1. 模块使用方式

CommonJS :require 引用模块    module.exports 暴露接口

            AMD:require 引用模块   使用 define 函数的 return 暴露接口

            CMD:require 引用模块   module.exports 或 exports 暴露接口

             ES6:import 引用模块     export default 暴露接口

2. 模块加载方式

CommonJS :运行时加载,一个模块就是一个对象,加载一个模块就是加载这个模块的所有方法,然后读取其中需要的方法。

            AMD:并行加载,提前执行。

            CMD:并行加载,按需执行。

            ES6 :编译时加载,在模块编译时就完成加载,引用的时候只加载需要的方法,其他方法不加载。

3. 实现模块规范

CommonJS :NodeJS

            AMD:RequireJS

            CMD:SeaJS

            ES6 :原生JS           

4. 输出的是引用还是拷贝

CommonJS :输出的是值的拷贝,不会受其内部影响。

            ES6 :输出的是值的引用,会受其内部影响。

5. 模块内顶层 this 对象

CommonJS :指向的是当前模块对象。

            ES6 :指向的是 undefined。

常见模块开发规范(CommonJS、AMD、CMD 以及 ES6 模块加载)详解_第1张图片

你可能感兴趣的:(其他)