seajsソースの解読


簡単な紹介
seajsは先端アプリケーションをモジュール化して開発した優れたソリューションです.多くの人が協力して開発した複雑で巨大な先端プロジェクトに特に役に立つ.簡単な紹介は多くなくて、皆さんはseajsのウェブサイトseajs.orgに行って紹介を参照してください.本文は主にseajsのソースコードとモジュール化の原理を簡単に解読します.説明の実際的でないところがあれば、ご指摘と交流をお願いします.注:本稿の解析はseajsの2.2.1バージョンに基づいています.
ディレクトリ構造
解凍後のsrcディレクトリ構造は以下の通りである.
intro.js             --       
sea.js               --       

util-lang.js         --     
util-events.js       --       
util-path.js         --     
util-request.js      -- HTTP   
util-deps.js         --     

module.js            --     
config.js            --   
outro.js             --       
srcディレクトリには、主なseajsソースコードが格納されています.各ファイルの役割も上記の通りです.この中で、module.jsは今回のソースコードの解読の核心ですが、他のファイルの役割も紹介します.sea.jsはコードに対して比較的に簡単で、実は全体のseajsの名前空間を声明します.intro.jsとoutro.jsは私たちがよく知っている匿名関数で基本コードを包む方式です.ここでは特に、この匿名関数はintro.jsとoutro.jsの2つのファイルに分割されます.このようなやり方は主にデバッグに便利で、デバッグの環境下で、intro.jsとoutro.jsを引用しないで、直接に大域でseajs内部のインターフェースを暴露することができて、デバッグしやすいです.intro.jsとoutro.jsを統合したコードは以下の通りです.
(function(global, undefined) {
    if (global.seajs) {
      return
    }
    // ....
})(this);
他のファイルの用途はいちいち繰り返しません.リストを見てください.
ページはどのようにダイナミックにjsファイルをロードしますか?
seajsのソースコードと原理を解析する前に、seajsやrequirejsがない場合、一番原始的な動的脚本のロード方法はどうですか?方法は簡単です.実はscriptのラベルを作成して、srcがあなたのためにロードしたいスクリプトurlを設置して、scriptタグアプリをDomの中に入れて考えました.そうです.ほとんどのモジュールがjsライブラリをロードする原理は全部そうです.
var script = document.createElement('script');
script.setAttribute('src', 'example.js');
script.onload = function() {
    console.log("script loaded!");
};
document.body.appendChild(script);
上記のコードは簡単なダイナミックスクリプトのロードを完了することができます.しかし、seajsの真の核心はモジュール依存の問題を処理することにある.先端のJS開発分野、特に複雑なwebアプリケーションでは、モジュール依存問題が頭を悩ませています.A、B、C、Dの4つのモジュールは、A.js、B.js、C.js、D.jsの4つのファイルに対応しています.彼らの間の依存関係は、例えば以下のようなものである.
  • A依存B
  • B依存CおよびD
  • 問題は、モジュール内の依存関係をどうやって探し出すか、Aが運行前にBをロードしたかなどです.これらは先端モジュール化とモジュール依存性の問題です.
    モジュール化の考え方
    seajsのモジュール化実現原理は、簡単とは言えないが、複雑といえば複雑ではない.主な考え方は下記のコードで説明できます.
    Module.define = function (id, deps, factory) {
        //             
        deps = parseDependencies(factory.toString());
        //   
        Module.save();
        //    url
        var url = Module.resolve(id);
        //     
        script.url = url;
        loadScript();
        //   factory        
        ...
    };
    
    コード内の宣言の依存性を取得します.
    まず、コードに依存するモジュールをどうやって取得するかを確認します.一般的に、seajsにおける同期ローディングモジュールの書き方は同じです.

    define('scripts/a', function(require, exports, module) { var factory = function() { var moduleB = require('scripts/b'); ... }; module.exports = factory; });
    依存情報を取得する必要があります.FunctionのtoString方法を利用して、関数自体のコードを返す方法です.正規表現だけがrequireキーワードの後の引用関係にマッチする必要があります.だからseajsの関数parse Dependenciesの書き方はこのようです.(この部分のコードはutil-deps.jsにあります.)
    var SLASH_RE = /\\\\/g
    var REQUIRE_RE = /"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\/\*[\S\s]*?\*\/|\/(?:\\\/|[^\/\r
    ])+\/(?=[^\/])|\/\/.*|\.\s*require|(?:^|[^$])\brequire\s*\(\s*(["'])(.+?)\1\s*\)/g function parseDependencies(code) { var ret = [] code.replace(SLASH_RE, "") // require , .replace(REQUIRE_RE, function(m, m1, m2) { if (m2) { ret.push(m2) } }) return ret }
    IDでシナリオのurlアドレスにマッチします.
    コード内で宣言されている依存IDを探し出して、正しいスクリプトurlアドレスにIDで一致させます.この部分のコードはutil-path.jsにあります.
    function id2Uri(id, refUri) {
      if (!id) return ""
    
      id = parseAlias(id)
      id = parsePaths(id)
      id = parseVars(id)
      id = normalize(id)
    
      var uri = addBase(id, refUri)
      uri = parseMap(uri)
    
      return uri
    }
    
    ここには特別なところがあります.require('a/b/c')のような書き方で、seajsはどうやってスクリプトの住所の絶対パスを知っていますか?理由は簡単で、seajsを通じて自分でdomに追加したidが'seajsnode'のscriptノードまたは現在のhtmlの中の最後のscriptノードであり、これらのノードのsrc属性を通じてシナリオの絶対パスを取得することです.
    モジュールロードプロセス
    核心のmodule.jsに目を移しましょう.seajsはモジュールのロードプロセスのために6つの状態を定義しています.
    var STATUS = Module.STATUS = {
      // 1 - The `module.uri` is being fetched
      FETCHING: 1,
      // 2 - The meta data has been saved to cachedMods
      SAVED: 2,
      // 3 - The `module.dependencies` are being loaded
      LOADING: 3,
      // 4 - The module are ready to execute
      LOADED: 4,
      // 5 - The module is being executed
      EXECUTING: 5,
      // 6 - The `module.exports` is available
      EXECUTED: 6
    }
    
    すなわち、*FETCHINGは、現在のモジュールのロードが完了し、モジュールデータを保存している*SAVEDから、依存モジュールのロードを開始しました.
    実際には、このロード実行プロセスは線形ではなく、現在のモジュールが依存するモジュールをロードするには、依存するモジュールも同様にこのプロセスを行う必要があります.すべての依存度がロードされて実行が完了するまで、現在のモジュールは実行を開始します.
    module.jsにおけるseajsのいくつかの方法は、上述の全体の流れを説明している.
  • LOADINGはfactoryがないモジュールを構築し、全体のロードフローを開始し、状態をFETCHINGからSAVDに初期化する.
  • LOADEDは、ロード方法により、サブモジュールのロードを開始し、状態はSAVEDからLOADINGに至る.
  • EXECUTINGは、サブモジュールが全部ロードされた後にワンロード方法を呼び出し、状態はLOADINGからLOADEDに至る.
  • EXECUTEDロードプロセスは全部終了しました.モジュールの実行を開始します.状態はEXECUTINGからEXECUTEDまでです.
  • ここの各方法の詳しい過程は一つ一つ解析しません.興味のある学生はソースコードを見に行きます.実際、seajsはロードしたモジュールに対して一部の引用を保存します.cachedModsでは、requireの時に先にキャッシュ中のモジュールを呼び出します.
    seajs.require = function(id) {
      var mod = Module.get(Module.resolve(id))
      if (mod.status < STATUS.EXECUTING) {
        mod.onload()
        mod.exec()
      }
      return mod.exports
    }
    Module.get = function(uri, deps) {
      return cachedMods[uri] || (cachedMods[uri] = new Module(uri, deps))
    }
    
    締め括りをつける
    先端モジュール化は常に先端開発において重要なポイントです.先端開発は他の言語に比べて特殊で、特に大規模なWebプロジェクトの先端コードに対応し、モジュールを簡潔かつ優雅に分割する方法、これらのモジュールの依存性をどう管理するかについては、一定の時間をかけて認識し、検討する必要があります.そのため、Common.js(JavaScript APIの設計、企画、標準化に取り組む)の誕生は、「JavaScriptモジュール化の時代」を開いています.先端領域のモジュール化方案は、requireJS、SeaJSなどのように、Common.jsの実践者であり、先端コードを計画するのに役立ちます.しかし、問題はまだたくさんあります.Seajsはまだ先端モジュール開発を完全に満足していません.性能問題、包装配置などの方法にはまだ不足があります.でも、技術の未来はいつも進歩しています.これからもっと良い解決方法があると信じています.
    参照
    http://island205.github.io/HelloSea.js/http://seajs.org/docs/#docshttp://chuansongme.com/account/wtp-notes