用一个例子读懂 RequireJS

用一个例子读懂 RequireJS

例子来自官方,我稍微改造了一下,如下:

// project.html

<!DOCTYPE html>

<html>

    <head>

        <title>requirejs</title>

        <!-- data-main attribute tells require.js to load

             scripts/main.js after require.js loads. -->

        <script data-main="scripts/main" src="scripts/require.js"></script>

    </head>

    <body>

    

    </body>

</html>

// scripts/main.js

define(function(util){

    require("helper/util");

    alert(‘main factory‘)

    return {a:1, b:2};

})

// scripts/helper/util.js

alert(‘util.js is loaded!‘)

三个文件,运行结果 "util.js is loaded" -> "main factory"。

data-main 属性有一个值 "scripts/main",表示当 require.js 加载完之后加载 scripts/main.js。

其实不仅如此,如果指定了这个属性,会把它的目录部分和文件部分拆开,即 scripts/ 和 main,之后会这么做:

  cfg.baseUrl = ‘scripts/‘;

  cfg.deps = [‘main‘];

即配置一下 baseUrl 和 deps

接着,用这个配置对象去初始化默认Context,这个过程会判断默认Context 是否依赖别的模块,这里明显依赖 "main",所以需要context.require(it)

1. 把需要加载的依赖放进一个数组

2. 遍历该数组,加载依赖,并轮询加载状态

append 的 script 节点需要提一下:

可以看到 RequireJS 为节点加了两个自定义属性,分别表示 contextName 和 moduleName

动态创建的 script 节点当脚本执行完后,会发出onload事件(IE 就是 onreadstatechange),RequireJS 这时会在事件处理函数中进行检测,加载的模块是否同时也存在依赖?来看一段代码:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

/**

 * 这个方法是在 script 节点加载的脚本执行完之后才会执行。

 * 1. 通过 event.target 或 event.srcElement 可以取到 script 节点

 * 2. 获取节点的 data-requiremodule 属性时,如这里的 "main"

 * 所以 moduleName 就是 "main"

 *

 * @param {String} moduleName

 */

completeLoad: function (moduleName) {

    var args;

    

    // 队列分为 全局队列 和 context队列

    //

    // 一个被加载的模块,在它的define()中,会把该模块对应的[name, deps, callback]塞进队列

    // 如果遵循一个文件一个模块的写法,队列里只有一个元素

    // 如果一个文件写了多个模块,那队列里有多个元素

    //

    // 现在的问题是:到底塞进哪个队列呢?

    // 因为 IE 通过 interactive 状态可以知道当前执行的 script 节点,

    // 而 script 节点又绑定了 data-requirecontext 属性,所以可以拿到contextName

    // 综上:IE 加入 context队列,非IE加入 全局队列

    //

    // 这句就表示把全局队列的元素加入context队列,并清空全局队列

    // 这样便实现了浏览器的兼容

    context.takeGlobalQueue();

    

    // defQueue 即 context 队列

    while (defQueue.length) {

        args = defQueue.shift();

        if (args[0] === null) {

            // 如果[name, deps, callback]中name为null,即匿名模块

            args[0] = moduleName;

            break;

        else if (args[0] === moduleName) {

            //Found matching define call for this script!

            break;

        else {

            // 如果一个文件出现多个define,才有可能进到这里,暂时可以无视这个分支

            callDefMain(args);

            args = null;

        }

    }

    

    // callDefMain其实是main()的apply调用

    // 它是定义模块的主函数,通过[name, deps, callback]构造模块

    // 它会获取模块需要的依赖,如果是未加载的依赖,会加入context.paused数组

    if (args) {

        callDefMain(args);

    else {

        // 如果加载的文件没有写成模块的形式,进到这里

        callDefMain([moduleName, [], null]);

    }

    // 每加载完一个,context.scriptCount就-1

    // 对浏览器来说,这没什么问题,但这有一个副作用

    // checkLoaded() 会通过scriptCount判断是否要轮询加载状态

    // 为了避免这个开销, 这里先-1

    if (req.isAsync) {

        context.scriptCount -= 1;

    }

    

    // 这个方法主要就是处理context.paused,即加载那些依赖

    // 并会轮询是否完成加载,并在加载完成时,做一些事

    resume();

    if (!req.isAsync) {

        context.scriptCount -= 1;

    }

}

时间: 2024-10-23 18:13:53

用一个例子读懂 RequireJS的相关文章

一个例子读懂 JS 异步编程: Callback / Promise / Generator / Async

JS异步编程实践理解 回顾JS异步编程方法的发展,主要有以下几种方式: Callback Promise Generator Async 需求 显示购物车商品列表的页面,用户可以勾选想要删除商品(单选或多选),点击确认删除按钮后,将已勾选的商品清除购物车,页面显示剩余商品. 为了便于本文内容阐述,假设后端没有提供一个批量删除商品的接口,所以对用户选择的商品列表,需要逐个调用删除接口. 用一个定时器代表一次接口请求.那思路就是遍历存放用户已选择商品的id数组,逐个发起删除请求del,待全部删除完成

一个例子看懂socket

一个服务端与多个客户端交互的例子 package j2se.socket; import java.io.*; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; /**  * Created by jingqing.zhou on 2015/6/17.  * 基于tcp协议的socket通信,实现用户登录  * 服务器端  */ public class server {     

一个例子搞懂C++的虚函数和纯虚函数

转自https://blog.csdn.net/vincent040/article/details/78848322,并对代码做了小幅修正,在此感谢原作者. 学习C++的多态性,你必然听过虚函数的概念,你必然知道有关她的种种语法,但你未必了解她为什么要那样做,未必了解她种种行为背后的所思所想.深知你不想在流于表面语法上的蜻蜓点水似是而非,今天我们就一起来揭开挡在你和虚函数(女神)之间的这一层窗户纸. 首先,我们要搞清楚女神的所作所为,即语法规范.然后再去探究她背后的逻辑道理.她的语法说来也不复

一个例子看懂神马是闭包

闭包就是这个玩意儿.怎么用在此不做多讲.就告诉大家闭包长神马样子. <!DOCTYPE html> <html> <head> <title>神马JS闭包</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <script> function wenyi(){ var i=0; ret

一个例子搞懂C语言的局部变量与全局变量

#include <stdio.h> int x = 77; /*全局变量x*/ void fn1() { extern int y; /*使用外部全局变量y,如果不加extern编译不通过*/ printf("fn1(): x=%d,y=%d\n",x,y);  //x=77,y=88; } void fn2() { extern int y; /*使用外部全局变量y,如果不加extern编译不通过*/ y=888; /*修改外部全局变量y为888*/ printf(&q

一个例子看懂异步代码执行效率

异步代码采用线程池,提供代码执行的并行性,不阻塞当前线程,实例代码,模拟三个耗时操作,分别耗时为1000.1500.1800ms,提供同步与异步的实现方式,Main中以同步异步的方式执行,对比执行时间,同步执行方式为各个方法的执行时间总和,而异步执行方式为最长的那个时间.ps:实际执行情况可能有其他的一些微不足道开销,但大体能反应异步的执行效率. class OutHelper { public DateTime Method1() { Thread.Sleep(1000); return Da

一个例子看懂C语言中的++号的用法

话不多说,直接上代码: #include<stdio.h> int main(){ int suzu [10] ={10,11,12,13},i; int *p=suzu; //打印出所有的数据 for(i=0;i<10;i++) printf("%d\n",suzu[i]); //因为*的优先级高于+号 所以这个括号是必须要的 printf("\n"); //因为这个不是按照指针来读取的,所以我们会从默认第一位开始读取 for(i=0;i<

通过一个案例彻底读懂10046 trace--字节级深入破解

转载请注明出处:http://blog.csdn.net/guoyjoe/article/details/37840583 2014.7.23晚20:30 Oracle support组猫大师分享<通过一个案例彻底读懂10046 trace--字节级深入破解> 如需了解很多其它课程请登录站点http://www.jianfengedu.com/Discuz/detail/id/56[技术分享QQ交流群]  252296815 国内首创成就QTune的顶级高手之课    -->我保证.认真

读懂源码:一步一步实现一个 Vue

源码阅读:究竟怎样才算是读懂了? 市面上有很多源码分析的文章,就我看到的而言,基本的套路就是梳理流程,讲一讲每个模块的功能,整篇文章有一大半都是直接挂源码.我不禁怀疑,作者真的看懂了吗?为什么我看完后还是什么都不懂呢? 事实上一个经过无数次版本迭代的框架源码并不适合初学者直接阅读,因为里面有太多细节,太多噪点,太多枝枝蔓蔓.要想真正理解框架的核心逻辑,必须剥茧抽丝,还原出一个纯净的雏形.如同 jQuery 最早的版本只有六百多行,我相信 Vue 的核心功能也只需要几百行就能实现.所以,读懂源码的