之前在《模块化:CommonJS规范》文中对CMD规范进行了介绍,并给出了服务端和浏览器端基于CommonJS模块化规范构建项目和模块化开发的示例demo。严格来讲,CommonJS这种模块化规范更加适用于服务器端编程,由于是同步的加载方式,并不十分适合浏览器端项目开发。
而此处介绍的AMD(Asynchronous Module Definition)-异步模块定义的规范——简称AMD规范,则采用异步方式加载模块,不会阻塞浏览器展示页面时,dom构建、css渲染等其它任务,模块内部的内容则采用同步加载方式,加载完成之后会立即执行主模块中定义的回调函数,真正启动web应用程序。
目录
AMD:基本语法
模块定义语法
模块引入语法
非AMD的传统模块化开发模式
早期模块化开发:IIFE模式+外部依赖引入
早期模块化开发:优缺点
AMD模块化开发模式
示例demo构建与测试
AMD模块化开发:AMD模块引入
AMD模块化开发:非AMD模块引入
AMD:基本语法
AMD作为一种模块化规范,有其对应的第三方库作为具体实现,最常用的就是RequireJS。也就是说,AMD规范的应用,需要借助RequireJS第三方库才能实现,点击此处,可访问其官网。
模块定义语法
AMD模块定义通过define语句实现,通常在开发过程中,基于define语法定义的模块可分为两类,
①没有依赖的模块,其语法如下,回调函数的内容即为自定义的模块的内容,
define(function(){//todo:定义模块内容 //返回模块对象return 模块;})
②定义有依赖的模块,其语法为如下,
define(['module1',...,'modulen'],function(param) {//todo:定义模块内容 //返回模块对象return 模块;
});
其中:
[1] 参数1:数组形式的字符串,显示声明要注入的若干依赖模块;
[2] 参数2:param-回调函数,在模块加载之后会立即执行。
模块引入语法
通常是在主模块开头部分,会使用RequireJS库提供的requirejs.config()、requirejs()接口分别实现子模块的路径配置与非AMD模块到AMD模块的映射、子模块的加载和主模块内容的定义。
代码示例及其注释如下,
//配置子模块及其路径requirejs.config({baseUrl: 'js', //相对根目录的路劲//默认相对于当前main.js主模块的相对路径paths: {dataService:'./modules/dataService',alert:'./modules/alert',jquery: './libs/jquery-3.6.1', //jquery默认支持amd规范angular: './libs/angular', //angular.js-非amd规范的模块},shim:{//配置非amd规范的js脚本库-将当前非amd文件规范化为AMD模块angular: {exports:'angular',}}});//引入子模块requirejs(['alert','jquery','angular'],function (alert,$,angular){alert.showMsg();const domTitle = $('#title')[0].innerText;console.log(domTitle);console.log(angular)});
非AMD的传统模块化开发模式
在出现RequireJS等AMD规范具体实现的第三方库之前,也已经诞生了不少模块化开发的方法,较为经典的就是“基于立即执行函数实现的模块模式开发”,也即:IIFE匿名闭包升级版本。在正式介绍AMD规范之前,有必要介绍一下早期的“模块模式”实现的模块化开发,及其缺点所在。
早期模块化开发:IIFE模式+外部依赖引入
①创建一个项目,其基本结构如下,其中:app.js为主模块、alert.js和dataService.js为子模块,index.html为将要引入主模块app.js的主页面。
②编写主模块和子模块的内容,
③在index.html主文件中引入app.js主模块,
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
</body>
<!--引入依赖模块-->
<script src="js/dataService.js"></script>
<script src="js/alert.js"></script>
<!--引入主模块-->
<script src="app.js"></script>
</html>
最终显示的内容如下,
早期模块化开发:优缺点
这种早期的借助立即执行函数实现的IIFE+外部依赖的模块化开发方式,其实就是现代模块化开发规范,包括ES6模块化规范的基石。其最大的优点就是:纯原生框架,无需借助任何第三方框架即可实现项目的模块化构建;其缺点是:需要在index.html主文件中引入主模块,以及主模块所依赖的所有的子模块,那么,在初始化index.html页面时,就需要发送若干次js文件的请求;并且还要保证子模块的引入顺序,否则就会出错。 对于大型项目来说,后期整体会变得越来越臃肿,甚至难以维护,因为模块之间的关系可能复杂到无法理解。
AMD模块化开发模式
前面已经提到,AMD只是一种开发规范,而RequireJS第三方JavaScript脚本库对其进行了实现。因此,Require.js作为一个异步的JavaScript模块加载器,下面的示例程序,必须要引入这个js脚本,可到官网进行下载:RequireJS。
示例demo构建与测试
①搭建如下的项目框架,基本结构如下图。
其中:js/libs-用来存放所依赖的第三方js脚本库;js/modules-用来存放自定义的AMD子模块;main.js-是Web应用程序的子模块;index.html-是Web应用程序的主页面。
②定义主模块和子模块,如下图所示,
其中,主模块main.js的内容如下,
//主模块
(function (){//配置子模块及其路径requirejs.config({baseUrl: 'js', //相对根目录的路劲//默认相对于当前main.js主模块的相对路径paths: {//引入自定义模块dataService:'./modules/dataService',alert:'./modules/alert',//引入主模块jquery: './libs/jquery-3.6.1'}});//引入子模块requirejs(['alert','jquery'],function (alert,$){alert.showMsg();const domTitle = $('#title')[0].innerText;console.log(domTitle);});
})();
③下载并在index.html文件中引入Require.js依赖库、主模块main.js,
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>AMD规范</title><!-- 引入子模块--><script data-main="js/main.js" src="js/libs/require.js"></script>
</head>
<body><div id="title">AMD规范实现</div>
</body>
</html>
④最终的实现效果如下,
AMD模块化开发:AMD模块引入
AMD模块化开发方式遇到的首要问题就是,如何引入自定义的、或者是第三方AMD子模块?
一般都是通过require.config()接口来实现的,示例代码如下,
//主模块
(function (){//配置子模块及其路径requirejs.config({baseUrl: 'js', //相对根目录的路劲//默认相对于当前main.js主模块的相对路径paths: {dataService:'./modules/dataService',alert:'./modules/alert',jquery: './libs/jquery-3.6.1', //jquery默认支持amd规范},});//引入子模块requirejs(['alert','jquery'],function (alert,$){alert.showMsg();const domTitle = $('#title')[0].innerText;console.log(domTitle);});
})();
上面的代码部分,baseUrl定义了子模块所在的根路径,这个路径是相对于项目的根路径来讲的;而paths参数部分,则对应子模块的配置信息。主要是子模块的模块id名称、与相对于baseUrl基路径的相对路径配置。
配置完毕之后,就可以在requirejs()接口中的第1个参数未知,加载需要的子模块,并将其作为回调函数的参数,就可以使用了。
AMD模块化开发:非AMD模块引入
在实际的开发中,我们遇到的有些第三方模块可能不是遵循AMD规范的,这时候就可以通过require.config()接口的shim参数将其映射为AMD模块。示例代码如下,将angular.js-非AMD第三方模块映射为AMD模块,并进行使用.
//主模块
(function (){//配置子模块及其路径requirejs.config({baseUrl: 'js', //相对根目录的路劲//默认相对于当前main.js主模块的相对路径paths: {dataService:'./modules/dataService',alert:'./modules/alert',jquery: './libs/jquery-3.6.1', //jquery默认支持amd规范angular: './libs/angular', //angular.js-非amd规范的模块},shim:{//配置非amd规范的js脚本库-将当前非amd文件规范化为AMD模块angular: {exports:'angular',}}});//引入子模块requirejs(['alert','jquery','angular'],function (alert,$,angular){alert.showMsg();const domTitle = $('#title')[0].innerText;console.log(domTitle);console.log(angular)});
})();