最近在研究前端模块化,大多数文章都提到了 AMD
,关于 AMD
的模块标准(或者说规范)讲的都很清楚,什么异步加载、保证正确顺序,对付八股很够,但是唯独没有关于 AMD
的实操工程,大多数是纸上谈兵,所以经过我的博览群书(其实是翻垃圾桶,十个九个抄)终于找到了在 AMD
那个年代的工程和一些简单实现)
AMD
AMD(Asynchronous Module Definition) - 异步模块定义,AMD
是一个模块规范(或者说标准),它其实是 CommonJS
规范的分支,这也就是为什么很多文章里提到 CommonJS
时往往会带上 AMD
定义
使用 AMD
规范的模块要求如下
define(id?, dependencies?, factory);
id
是名称dependencies
是依赖factory
是模块逻辑
以上仅为简述,完整的 AMD
介绍请参考文档
实现
规范只是理论而不是实现,像 CommonJS
规范就有很多实现,其中最出名的就是 Node.js
,还有比较经典的理论和实现的例子有 Promise A+
规范和一系列的 Promise
实现,比如浏览器的 ES6 Promise
原生实现、开源实现 kriskowal/q...,而 AMD
的实现就是在 2009
年 2
月的推出的 RequireJS
文章例子就是基于 RequireJS
的工程实现
RequireJS 的使用
RequireJS
源于 CommonJS
但后面又从其中独立出去成立了新社区,因此没有和 CommonJS
的实现 Node.js
及其生态(npm
)产生太多联系,也就是说 RequireJS
的项目是不需要 Node.js
环境的
文章使用的是 require.js@2.3.6
文章实现的是通过 JS
传递颜色名称并将对应的十六进制颜色渲染到 DOM
中,其中文件结构
├── app - 项目代码
| ├── utils.js - 工具模块
| └── main.js - 主逻辑
├── lib - 第三方库
| ├── draw-dom.js
| └── require.js
├── app.js - 入口文件(包含 require.js 的配置)
├── index.html
目录结构基于 RequireJS
官网给出的文档中的单页应用的例子(2011
年就有单页应用的说法了!)
<!-- index.html -->
<head>
<script data-main="app.js" src="lib/require.js" async></script>
</head>
<body>
<h1 id="app">amd-learning</h1>
</body>
入口
入口文件主要是配置 require.js
和加载入口函数
// app.js
require.config({
baseUrl: "lib",
paths: {
app: "../app",
},
});
require(["app/main"]);
baseUrl
用于指定第三方模块的位置,后期在主逻辑中可以直接通过名称调用,比如lib
目录下的draw-dom
模块require(draw-dom)
paths
用来指定用户自定义模块,即源代码部分
自定义模块
目录中的 utils
就是自定义模块,在主逻辑中可以根据相对路径去引用,这一点和 CommonJS
保持一致
define(function (require) {
const drawDOM = require("draw-dom");
const utils = require("./utils");
drawDOM.draw("app", utils.translate("red"));
});
注意,在引用模块时必须使用 require
,而 require
由 define
函数的参数传入(这是 require.js
的默认模块)
而自定义模块导出可以选择返回一个对象作为导出值
如果工厂函数返回一个值(一个对象、函数或任何 Truthy 值),那么该值应该被分配为模块的导出值
如下
// utils.js
define(function () {
return {
translate: function (name) {
let color = "";
switch (name) {
case "red":
color = "#FF0000";
break;
default:
color = "#000000";
}
return color;
},
};
});
第三方模块
require.js
使用的模块需要在模块源码提供处下载,此处放置在 lib
目录统一管理,为了便于描述我直接手撸了一个
define("draw-dom", ["exports"], function (exports) {
exports.draw = function (id, color) {
document.querySelector(String("#" + id)).style.color = color;
};
});
注意,导出可以使用 exports
模块(exports
和 require
一样都是 require.js
的默认模块)
最后例子的运行效果如下