RequireJS源碼初探

来源:http://www.cnblogs.com/xing901022/archive/2016/04/17/5401195.html
-Advertisement-
Play Games

前兩天跟著葉小釵的博客,看了下RequireJS的源碼,大體瞭解了其中的執行過程。不過在何時進行依賴項的載入,以及具體的代碼在何處執行,還沒有搞透徹,奈何能力不夠,只能先記錄一下了。 RequireJS的初探 看源碼從頭開始看,肯定是不切實際的。按照葉小釵的方法,是從data main開始的,所以我 ...


前兩天跟著葉小釵的博客,看了下RequireJS的源碼,大體瞭解了其中的執行過程。不過在何時進行依賴項的載入,以及具體的代碼在何處執行,還沒有搞透徹,奈何能力不夠,只能先記錄一下了。

RequireJS的初探

看源碼從頭開始看,肯定是不切實際的。按照葉小釵的方法,是從data-main開始的,所以我們也從那裡開始把!

首先,頁面會有一段js標簽,會去載入requirejs:

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

Requirejs中,代碼是一個自執行的方法:

var requirejs,require,define;
(function(global){
    
})(this);

源碼中,主要是定義了三個全局的變數——requirejs,require,define,下麵是一個自執行的方法。

那麼主要就是看看這個方法裡面都幹了什麼吧!

RequireJS主體方法

//定義環境變數

    //定義各種方法

    //檢查requirejs,require,define

    //核心部分
    function newContext(){}//定義核心部分方法
    
    req = requirejs = function(){//定義req
        //...
        return context.require();
    };

    req.config = function(){};

    req({});//創建預設的上下文

    req.createNode = function (config, moduleName, url) {
        var node = config.xhtml ?
                document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') :
                document.createElement('script');
        node.type = config.scriptType || 'text/javascript';
        node.charset = 'utf-8';
        node.async = true;
        return node;
    };

    //洋洋灑灑,載入代碼
    req.load = function(){
        node = req.createNode(config, moduleName, url);//創建節點
        node.addEventListener('load', context.onScriptLoad, false);//添加load事件
        if (baseElement) {//插入到head裡面
            head.insertBefore(node, baseElement);
        } else {
            head.appendChild(node);
        }
    };

    if (isBrowser && !cfg.skipDataMain) {
        //載入main.js
    }

    define = function(){};

    req.exec =function(){};

    req(cfg);//執行配置文件

上面的代碼中,關鍵的方法定義其實只有兩個:

  • 定義了newContext()方法,用於配置上下文環境,並且僅會執行一次!後續都是使用同一個context!
  • 定義req,它是後續使用的方法!

然後在上面的代碼中,它做了下麵三件事:

  • 1 執行req({}),傳入了空的對象,初始化context
  • 2 if(isBrowser && xxxx)....,載入data-main所指向的js,讀取配置
  • 3 執行req(cfg),執行剛剛讀取的配置,載入目標模塊...

基本上就是這個套路了!

newContext()

RequireJS最精彩的部分,就在這個方法裡面了!

function newContext(contextName){

    function getModule(depMap) {
        var id = depMap.id,
            mod = getOwn(registry, id);

        if (!mod) {
            mod = registry[id] = new context.Module(depMap);
        }

        return mod;
    }
    
    function checkLoaded() {
        
    }

    context = {
        //...
        makeRequire: function (relMap, options) {
            //核心
            function localRequire(deps, callback, errback) {
                //真正的核心
                context.nextTick(function () {
                    intakeDefines();

                    requireMod = getModule(makeModuleMap(null, relMap));//主要看這裡吧

                    requireMod.skipMap = options.skipMap;

                    requireMod.init(deps, callback, errback, {
                        enabled: true
                    });

                    checkLoaded();
                });
            }

            return localRequire;
        },
        load: function (id, url) {
            req.load(context, id, url);
        },
        onScriptLoad : function() {
            context.completeLoad();
        },
        completeLoad : function() {
            takeGlobalQueue();
            while(defQueue.length){

            }
            mod = getOwn(registry, moduleName);
            checkLoaded();
        }
        //...
    }

    Module.prototype = {
        init : function(depMaps, factory, errback, options){
            if (options.enabled || this.enabled) {
                this.enable();
            } else {
                this.check();
            }
        },
        fetch : function(){
            if (this.shim) {//依賴
                context.makeRequire(this.map, {
                    enableBuildCallback: true
                })(this.shim.deps || [], bind(this, function () {
                    return map.prefix ? this.callPlugin() : this.load();
                }));
            } else {
                //Regular dependency.
                return map.prefix ? this.callPlugin() : this.load();//是否包含首碼 text!xxx
            }
        },
        load: function () {
            var url = this.map.url;

            //Regular dependency.
            if (!urlFetched[url]) {
                urlFetched[url] = true;
                context.load(this.map.id, url);
            }
        },
        check : function(){
            this.fetch();
        },
        enable : function(){
            this.check();
        }
    };
    context.require = context.makeRequire();//其實是把localRequire賦值給context.require
    return context;
};

這個newContext()裡面定義大量的載入模塊、校驗、檢查等工作。可以看到這個方法,主要是定義了一個context對象和Module方法。

然後執行這個方法後,會自動調用context對象的makeRequire()方法,這個makeRequire實際上調用的又是內部定義的localRequire()。LocalRequire則是處理載入任務的核心——比如依賴的檢查,模塊的載入等等。

執行點

req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) {
    setTimeout(fn, 4);
} : function (fn) { fn(); };

所有的載入都會交由這個nextTick執行,暫時沒有搞清楚...

流程圖

收穫

  • 1 原來RequireJS載入模塊的時候,是檢查data-main屬性,然後去載入目標js。
  • 2 載入到目標模塊後,會按照它的依賴關係,進行載入,並且每個模塊僅會載入一次。
  • 3 載入模塊的時候,會綁定一個load事件,當載入完會觸發事件,執行該js
  • 4 腳本實際上是通過創建了頁面的script元素,然後添加到head裡面。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 字元x 字元 x\\ 反斜線字元\0n 帶有八進位值 0 的字元 n (0 <= n <= 7)\0nn 帶有八進位值 0 的字元 nn (0 <= n <= 7)\0mnn 帶有八進位值 0 的字元 mnn(0 <= m <= 3、0 <= n <= 7)\xhh 帶有十六進位值 0x 的字元 h ...
  • 對IE瀏覽器尤其是IE6的抱怨基本已進入麻痹狀態,偶爾甚至產生非常消極的想法:這個世界只有一個瀏覽器就好了,哪怕這唯一的瀏覽器就是IE6。當然,這樣的想法是非常病態的,馬上打消。本文裡面,介紹了10個很實但IE卻不支持的CSS屬性,列出這些屬性並不是為了數落IE(數落也沒用),而是你瞭解了哪些CSS ...
  • 數據綁定是在應用程式 UI 與數據源建立連接的過程。如果綁定正確數據,則當數據更改其值時,綁定到數據的UI屬性值會自動反映更改。DeviceOne支持靈活的數據綁定,使UI展示和數據可以清晰的分離。目前還不支持雙向綁定,只支持數據到展示的傳遞。使用DeviceOne開發App,你可以不使用任何數據b ...
  • 幾個知識點: HTML 指的是超文本標記語言 (Hyper Text Markup Language) HTML框架結構: <!DOCTYPE html> <html> <head> </head> <body> 此處為標簽內容 </body> </html> HTML屬性: class=“XXX” ...
  • 一、引言 之前這個系列文章已經介紹Bootstrap。由於最近項目中,前端是Asp.net MVC + KnockoutJs + Bootstrap來做的。所以我又重新開始寫這個系列。今天就讓我們來看看Web前端的MVVM框架——KnockoutJs。 二、KnockoutJs是什麼? 做.NET開 ...
  • 由於我所在的項目組一直在用gulp構建工具,而我只是在前人搭好的環境下每次運行gulp packJs來打包js,對裡面的東西全然不知,剛好最近有些時間就想自己從學學將gulp怎麼用於構建前端項目中,這樣也會對這個構建工具有一些深刻的理解。 首先,gulp是什麼?gulp是基於nodejs的自動任務運 ...
  • React是什麼?React.js 是 Facebook 推出的一個用來構建用戶界面的 JavaScript 庫。Facebook開源了React,這是該公司用於構建反應式圖形界面的JavaScript庫,已經應用於構建Instagram網站及 Facebook部分網站。最近出現了AngularJS ...
  • AngularJS 表達式 AngularJS 表達式寫在雙大括弧內:{{expression}} AngularJS 表達式把數據綁定到HTML,這與ng-bind 指令有異曲同工之妙 AngularJS 將在表達式書寫的位置輸出數據。 AngularJS 表達式很像JavaScript表達式:他 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...