Appearance
Requirejs
元信息
- 目标:理解社区在语言缺位年代发明的异步模块方案——AMD 规范与 require.js 加载器如何用 define/require + 动态 script 标签解决重用与名字冲突(历史地位,已被 ESM 取代)
- 关键概念:AMD、define、require、模块加载器、依赖注入、动态script
- 关联阶段:cp3
- 常见误区:以为 require.js 是新的构建工具(它运行时动态生成 script 标签,不做打包);以为 AMD 是语言标准(社区规范,最终答案是 ESM);以为传给回调的参数名必须与库名一致(命名由回调参数位置决定,故能消解 $ 冲突)
模块化的需求
前端项目日益复杂,需要来自多方的协作,催生如下需求
- 工程化需求
- 同一作者的代码之间的重用和共享
- 不同作者的代码之间的重用和共享
- 技术需求
- 将js代码库封装起来,只暴露指定的接口,即模块化
- 模块化需要遵循统一的规范,保证加载多个js代码模块,不会产生名称、作用域冲突等问题
- 提供统一的代码库的管理机制,如按名称查找、引用和加载
- 额外功能,如:版本兼容检查、扩展机制等
动态生成<script>标记
早期的js的模块化机制十分简单,只能依赖<script>标记进行加载,缺乏类似程序设计语言的import、package等管理机制
AMD规范 与 Require.js
- AMD: Asynchronous Module Definition 异步模块定义,是js中关于如何编写可共享代码库的一组规范
- require.js 是一个基于AMD规范实现的文件和模块加载器,能够异步地加载动态的依赖
换言之,你如果希望编写可被广泛共享的代码库,且被引入时不会产生变量名冲突之类的问题,则编码时应当遵循AMD规范,然后调用者使用 require.js 来加载你的代码库,就可以避免前述的诸多问题。
- requireJS使用document.body.appendChild()方法将每一个依赖加载为一个script标签。
- requireJS等待所有的依赖加载完毕,计算出各模块定义函数的正确调用顺序,然后依次调用它们。
AMD 规范示例
以下代码,定义了一个最简单的模块,该模块暴露一个名称为$的函数
javascript
// other-lib.js
define({
$: function(s){console.log(s)}
}
);结合 require.js 使用
- require.config定义了本地名字到文件实体的映射,即路由,如other => other-lib.js
- 基于路由,require函数将读取第一个参数中的库名字列表,然后依次加载对应的库文件
- 然后,基于AMD规范,将各库中
define()函数所返回的对象引用作为回调函数(即require的第二个参数)的参数。上例中:$源于jquery库的$,而other-lib库虽然也定义了$,但是在主程序中,它有了新的命名other.$()。这样,就有效地避免了名称冲突。
其它示例:待定的函数名
以下代码,定义了只有一个主函数的模块,其函数名待定
javascript
// other-lib.js
define(function () {
return function (s){ console.log(s) };
});javascript
//main.js
require(['jquery','other'],function($,other){
$('#info').text('该字段不能为空!');
other("this is from other-lib");
});other库暴露的主函数没有命名,但在main.js即调用者代码里,requirejs可将其映射为任意名称
小结
require.js部分解决了js代码的重用和共享的问题,其底层仍然使用了动态生成script标记的机制,最大程度的利用了浏览器自身的能力
- requirejs自身的代码就是一个不错示例 https://requirejs.org/docs/release/2.3.7/comments/require.js
- 参考文档 https://juejin.cn/post/7272709087250186299
其它模块化规范
- https://github.com/cmdjs/specification/blob/master/draft/module.md
- 前端模块化和AMD、CMD规范 https://www.cnblogs.com/highsea90/p/4383895.html
- AMD、CMD、UMD https://www.jianshu.com/p/bd4585b737d7