模块化开发是指在程序开发中,可以将每个功能脚本或文件单独提炼出来作为一个个独立的模块,模块之间相互关联,相互依赖,并对外暴露出一个入口文件。既方便开发者自己引用,也方便他人下载使用。比如前端经常用到的 Ant Design 组件,就可以看作是一个模块,在 Ant Design 中也按功能分了许多子模块,像轮播图组件、日期选择组件、导航组件等,模块化开发更便于使用和维护。
在传统的 HTML 应用中,脚本加载通过 script 标签的 src 属性,有几个脚本就引用几个资源。
这样加载资源,有几个缺陷;
script 标签提供了 defer 属性 和 async 属性来实现异步加载,不去阻塞 DOM 渲染。
虽然 script 标签提供了方式实现异步加载,不阻塞 DOM 渲染,但是其他的问题还是没有解决,而模块化开发就能有效的解决以上问题,常见的模块加载器有 CommonJS、CMD、AMD 以及 ES6 模块加载,本文将探讨每个模块加载器的概念、特点、使用方式以及适用场景。
1. 概念
由于 JavaScript 没有模块体系和相应的包管理工具,所以不具备大型应用开发能力,因此 CommonJS 应运而生,而用 JavaScript 开发的服务端语言 NodeJS 就实现了 CommonJS 的规范。
2. 特点
3. 模块使用方式
4. 举例
module.exports = path; //对外暴露接口
const path = require('path');//加载模块
5. 应用场景
CommonJS 是代码同步加载,因为服务端的模块都是放在硬盘,加载的时间就是硬盘的读取时间。所以适用于服务端,并不适用于浏览器。
1. 概念
上文说到 CommonJS 是同步加载,如果前面的程序加载时间太长,后面的程序要一直等待,在浏览器就会长时间出现空白现象,这对于现在越来越丰富的 Web 应用是无法忍受的,为了解决这个问题,AMD 规范就出现了,Require.js 是 AMD 规范的实现。
2. 特点
3. 模块使用方式
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 等环境使用。
1. 概念
CMD 是通用模块加载,Sea.js 是 CMD 的规范实现,CMD 规范要解决的问题与 AMD 一样,只不过是对依赖模块的执行时机不同 ,CMD 是模块需要用到的时候再去执行,推崇就近依赖。
2. 特点
3. 模块使用方式
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 浏览器
1. 概念
ES6 也可以实现浏览器的模块化加载,需要在 script 标签中加入 “type=module” 属性,浏览器看到这个属性就知道这是个 ES6模块,会等到 DOM 渲染完成,再执行该模块,如果有多个 “type=module” ,浏览器会按序加载,相当于添加了 defer 属性。
2. 特点
在 ES6 中模块化加载需要注意的几点:
3. 模块化使用方式
//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。