• 用一个例子读懂 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;
        }
    }
  • 相关阅读:
    我使用的Chrome插件列表
    从花式swap引出的pointer aliasing问题
    CF Educational Codeforces Round 10 D. Nested Segments 离散化+树状数组
    CF #335 div1 A. Sorting Railway Cars
    Mac 下载安装MySQL
    Mac 安装Tomcat
    CF #CROC 2016
    安全体系(零)—— 加解密算法、消息摘要、消息认证技术、数字签名与公钥证书
    安全体系(一)—— DES算法详解
    JAVA实现用户的权限管理
  • 原文地址:https://www.cnblogs.com/liangxiaoli/p/7011094.html
Copyright © 2020-2023  润新知