Skip to content

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等管理机制

import "./styles.css";

document.getElementById("app").innerHTML = `
<h1>Hello world</h1>
`;

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 使用

<!DOCTYPE html>
<html>
<head>
  <title>Parcel Sandbox</title>
  <meta charset="UTF-8" />
  <link rel="stylesheet" href="/styles.css" />
</head>
<body>
  <h1>Hello world</h1>
</body>
</html>
  • 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标记的机制,最大程度的利用了浏览器自身的能力

其它模块化规范

参考阅读