require 遵循 AMD 规范,对依赖模块进行异步加载,等待异步加载完成,再执行回调。
所以有如下好处:
- 天生异步
- 依赖容易被识别
- 避免全局变量
- 如果需要可以懒加载
- 容易管理和组织代码
这里不说 AMD 和 CMD,单纯讲讲在requirejs运行的几个关键点。
-
怎么去匹配一个模块的呢 ?
require( [ 'a', 'b' ], callback )
a, b 就是我们的 moduleId ;
id 和 path 之间有对应原则, 在 require 里面用一个 Map 对象以键值对形式保存, 这样加载器就能知道需要加载的路径 -
拿到模块的路径,怎么加载到页面 ?
拿到路径,就需要请求。通过 createElement('script') & appendChild 去请求。
当然也有可能有时会通过 ajax 去请求脚本内容。(ps: 什么时候会是 ajax 请求呢)
create 的<script>
标签会被 append 到页面的 head 头部。
同时会被打上两个标记:data-requiremodule
&data-requirecontext
.
data-requiremodule : 用来标识模块 id
data-requirecontext: 上下文环境 -
怎么定义一个模块,定义的模块怎么执行 ?
define( id?, dependencies?, factory )
当我们 define(factory) 多个这样的自定义匿名模块时, 被加载进页面, 怎么知道当前是当前被定义的模块呢?
刚才说了每个模块都有一个 id , 那么怎么知道这个模块的 id ?
通过document.currentScript
获取当前执行的<script>
,
从而获取到用于标识当前模块的属性data-requiremodule
.
ps 兼容处理: 通过script.onload
-
依赖解析
require 遵循 AMD 规范, 当进入一个 define / require 时候, 先加载完依赖确保能使用后, 才去执行 factory 回调, 并产出 export 接口(define).
那依赖是怎么解析的出来的呢 ?
简单的就是, 加载入口文件, 通过toString()
获得函数内容, 再用正则去匹配 moduleId, 得到子模块。
如果子模块还有依赖, 则会递归进行解析和加载 .