javascriptモジュール化CommonJS、AMD、CMD、UMD、ES 6


これはjsモジュール化プログラミングに関する総括記録です.
ウェブサイトが次第に「インターネットアプリケーション」になるにつれて、ウェブページに埋め込まれているJavascriptコードはますます巨大になり、ますます複雑になります.
ウェブページはますますデスクトッププログラムのようになっています.チームの分担、進捗管理、ユニットテストなどが必要です.開発者はソフトウェアエンジニアリングの方法を使って、ウェブページの業務ロジックを管理しなければなりません.
Javascriptはモジュール化されたプログラミングで、すでに切実な需要になっています.理想的には、開発者は核心的な業務ロジックを実現するだけで、他の人が書いたモジュールをロードすることができます.
Javascriptコミュニティは多くの努力をして、既存の運行環境の中で、「モジュール」の効果を実現します.
Common JS
ここのCommon JS規格はCommunJS Modules/1.0仕様を指します.
Common JSはサーバー側に偏りがある仕様です.NodeJSはこの仕様を採用しています.Common JSのモジュールはスクリプトファイルです.requireコマンドは台本を初めて読み込むとスクリプト全体を実行し、メモリ内にオブジェクトを生成します.
{
  id: '...',
  exports: { ... },
  loaded: true,
  ...
}
IDはモジュール名であり、exportsはモジュールが導き出すインターフェースであり、loadedはモジュールのロードが完了したかどうかを表しています.他にも多くの属性がありますが、ここでは省略されています.
今後このモジュールを使用する必要がある場合、exports属性に値を取ります.再度requireコマンドを実行しても、このモジュールは再実行されず、キャッシュに値を取ります.
// math.js
exports.add = function(a, b) {
  return a + b;
}
var math = require('math');
math.add(2, 3); // 5
Common JSは同期ローディングモジュールであるため、これはサーバー側にとっては問題ではなく、すべてのモジュールがローカルハードディスクに置かれているためです.モジュール待ち時間はハードディスクのファイル読み込み時間で、小さいです.しかし、ブラウザにとっては、サーバーからモジュールをロードする必要があります.インターネットの速度、代理などの理由があります.待ち時間が長いと、ブラウザは「仮死」状態になります.
したがって、ブラウザ側では、CommonJS仕様には適していません.だからブラウザの端にもう一つの仕様が現れました.
AMD
CommunJSはモジュール化の問題を解決しましたが、この同期ローディング方式はブラウザ側には適していません.
AMDは「Aynchronous Module Definition」の略語で、「非同期モジュール定義」です.モジュールを非同期的にロードします.モジュールのロードは、後のステートメントの動作に影響しません.ここで非同期とは、ブラウザの他のタスク(dom構築、cssレンダリングなど)をブロックしないことを指し、ロード内部は同期されている(モジュールをロードしたらすぐにフィードバックを実行する).
AMDはrequireコマンドでモジュールをロードしますが、CommunJSとは異なり、二つのパラメータが必要です.
require([module], callback);
一番目のパラメータは「module」です.中のメンバーはロードするモジュールです.calbackはロード完了後のコールバック関数です.上記のコードをAMD方式に変更した場合:
require(['math'], function(math) {
  math.add(2, 3);
})
ここで、パラメータは配列のメンバーに対応します.
requireJSロードモジュールは、AMD仕様を採用しています.つまり、モジュールはAMD規定の方式で書かなければなりません.
具体的には、モジュール書きは特定のdefine関数を用いて定義しなければならない.モジュールが他のモジュールに依存しない場合は、define関数に直接書き込むことができます.
define(id?, dependencies?, factory);
  • id:モジュールの名前で、このパラメータが提供されていない場合、モジュールの名前はデフォルトでモジュールキャリア要求の指定スクリプトの名前であるべきです.
  • dependencies:モジュールの依存性は、モジュールによって定義されたモジュールによって識別された配列の字面量です.依存パラメータは任意であり、このパラメータを無視すると、デフォルトは["require", "exports", "module"]であるべきである.しかし、工場法の長さ属性が3未満の場合、キャリアは関数の長さ属性で指定されたパラメータの個数で工場メソッドを呼び出すように選択されます.
  • factory:モジュールの工場関数であり、モジュールは実行する関数またはオブジェクトを初期化する.関数なら、一回だけ実行されるべきです.オブジェクトの場合、このオブジェクトはモジュールの出力値です.
  • 今はmath.jsファイルがあると仮定して、mathモジュールを定義しました.それでは、math.jsの書き方は以下の通りです.
    // math.js
    define(function() {
      var add = function(x, y) {
        return x + y;
      }
    
      return  {
        add: add
      }
    })
    ロード方法は以下の通りです.
    // main.js
    require(['math'], function(math) {
      alert(math.add(1, 1));
    })
    mathモジュールが他のモジュールに依存している場合は、次のように書きます.
    // math.js
    define(['dependenceModule'], function(dependenceModule) {
      // ...
    })
    require()関数がmathモジュールをロードすると、先にdependenceModuleモジュールをロードします.複数の依存性がある場合は、すべての依存性をdefine関数の最初のパラメータ配列に書くので、AMDは前置依存である.これはCMD仕様とは違って、近くに依存しています.
    CMD
    CMDは近くに依存することを推奨し、実行を遅延させる.あなたの依頼をコードの任意の行に書き込むことができます.以下の通りです.
    define(factory)
    
    factoryが関数の場合、モジュールの構造方法であることを示します.この構成方法を実行すると、モジュールが外部に提供するインターフェースが得られます.factoryメソッドは、実行時にデフォルトで三つのパラメータが入ります.require、exports、module.
    // CMD
    define(function(require, exports, module) {
      var a = require('./a');
      a.doSomething();
      var b = require('./b');
      b.doSomething();
    })
    AMDの書き方を使うと、次のようになります.
    // AMD
    define(['a', 'b'], function(a, b) {
      a.doSomething();
      b.doSomething();
    })
    この仕様は実際にSeajsの普及のために作られました.SeaJSを見てみます.基本的にはこの仕様が分かります.
    同様にSeajsもプリロード依存jsとAMDの仕様はプリロードされている点で同じであり、明らかに異なっているところはコールとステートメント依存のところである.AMDとCMDはdifineとrequireを使用していますが、CMD標準は使用中に依存する傾向があります.つまり、コードがどこに書いてあるかに関わらず、突然他のモジュールに依存する必要があります.今のコードでrequireを導入すればいいです.規範はプリローディングを解決してくれます.しかし、AMD標準は事前にパラメータ部分に頼って書かなければなりません.これが一番明らかな違いです.
    sea.jsはsea.use()を介してモジュールをロードする.
    seajs.use(id, callback?)
    UMD
    Common JSはサーバー側の仕様なので、AMD、CMDの2つの基準と実際には衝突しません.
    ファイルを書くときは、様々なロード仕様に対応する必要がありますが、どうすればいいですか?下のコードを見てください.
    (function (root, factory) {
    
        if (typeof define === 'function' && define.amd) {
    
            // AMD
    
            define(['jquery', 'underscore'], factory);
    
        } else if (typeof exports === 'object') {
    
            // Node, CommonJS   
    
            module.exports = factory(require('jquery'), require('underscore'));
    
        } else {
    
            //        (root   window)
    
            root.returnExports = factory(root.jQuery, root._);
    
        }
    
    }(this, function ($, _) {
    
        //   
    
        function a(){}; //     ,        (   )
    
        function b(){}; //     ,      
    
        function c(){}; //     ,      
    
    
    
        //       
    
        return {
    
            b: b,
    
            c: c
    
        }
    
    }));
    このコードは様々なロード仕様に対応できます.
    ES 6
    s 6は、モジュールの入出力をimportexportにより実現する.ここで、importコマンドは、他のモジュールが提供する機能を入力するために使用され、exportコマンドは、所定のモジュールの対外インターフェースに使用されます.
    export
    モジュールは独立したファイルです.ファイル内のすべての変数は、外部から取得できません.外部ファイルにこのモジュールの変数を読み込むためには、このモジュール内でexportキーを使って変数を導出する必要があります.例えば:
    // profile.js
    export var a = 1;
    export var b = 2;
    export var c = 3;
    下記の書き方は等価です.この方法はより明確です.
    var a = 1;
    var b = 2;
    var c = 3;
    export {a, b, c}
    exportコマンドは、変数を出力する以外に、関数やクラスを導出することができます.
  • 導出関数
  • export function foo(){}
    function foo(){}
    function bar(){}
    
    export {foo, bar as bar2}
    上記のasは、エクスポートされた変数に名前を変更します.
    なお、exportから導出された変数はファイルの最上階にしかありません.ブロックレベルのスコープ内にあるとエラーが発生します.例えば:
    function foo() {
      export 'bar'; // SyntaxError
    }
  • 導出クラス
  • export default class {} //   default    
    export文の出力値は、動的に結合され、そのモジュールを結合します.
    // foo.js
    export var foo = 'foo';
    
    setTimeout(function() {
      foo = 'foo2';
    }, 500);
    
    // main.js
    import * as m from './foo';
    
    console.log(m.foo); // foo
    setTimeout(() => console.log(m.foo), 500); // foo2
    import
    importコマンドは、他のモジュールのexportから導出された部分を導入することができます.
    // abc.js
    var a = 1;
    var b = 2;
    var c = 3;
    export {a, b, c}
    
    //main.js
    import {a, b, c} from './abc';
    console.log(a, b, c);
    導入した変数の名前を新たに取りたい場合は、asキーワードを使用します.
    import {a as aa, b, c};
    console.log(aa, b, c)
    モジュールに先に入力してモジュールを出力したい場合、import文はexport文と一緒に書くことができます.
    import {a, b, c} form './abc';
    export {a, b,  c}
    
    //     ,      ,   
    export {a, b, c} from './abc';
    モジュール全体のロード
    キーワードを使用します
    // abc.js
    export var a = 1;
    export var b = 2;
    export var c = 3;
    // main.js
    import * from as abc form './abc';
    console.log(abc.a, abc.b, abc.c);
    export default
    exportがコンテンツを出力する場合、複数の変数を同時に出力するには、大かっこ{}を使用する必要があり、同時に大かっこを導入する必要があります.export defalutを使って出力する場合は、大かっこは不要ですが、export defaultが出力する変数を入力する場合は、大かっこは不要です.
    // abc.js
    var a = 1, b = 2, c = 3;
    export {a, b};
    export default c;
    import {a, b} from './abc';
    import c from './abc'; //       
    console.log(a, b, c) // 1 2 3
    本質的には、export defaultが出力するのはdefaultという変数または方法であり、このdefault変数を入力するときは、大きな括弧は必要ない.
    // abc.js
    export {a as default};
    
    // main.js
    import a from './abc'; //        
    
    //        
    import {default as aa} from './abc';
    console.log(aa);
    ここまでにしましょう.サイクルローディング(モジュール相互依存)については書かれていませんが、CommunJSとES 6の処理方式は違います.
    参照
    AMD、CMD、CommonJS仕様をどう理解しますか?javascriptモジュール化ロード学習まとめ
    AMD/CMDと先端仕様
    フロントエンドモジュール化の旅(二):CommunJS、AMDとCMD
    javascriptのモジュール仕様(CommunJs/AMD/CMD)を研究します.
    Javascriptモジュール化プログラミング(一):モジュールの書き方
    Javascriptモジュール化プログラミング(二):AMD仕様
    Javascriptモジュール化プログラミング(三):require.jsの使い方
    Module