先端モジュール化開発について話します.

8266 ワード

JavaScriptの開発が一般化するにつれて、名前空間と依存性はますます処理しにくくなりました.フロントエンドの開発者はモジュール化してこの問題を処理します.この文章では、先端開発者が現在使っているモジュール化案と解決しようとする問題について検討します.
なぜJavaScriptモジュールが必要ですか?
モジュール化はあなたのコードを低結合させ、機能モジュールは直接に相互に影響しない.
  • メンテナンス可能性:各モジュールは個別に定義され、相互に独立している.モジュールはできるだけ外部との関係を清算し、独立してそれを維持し、改造することができます.一つのモジュールを維持するのは全体の中で論理判断を修正するよりずっといいです.
  • 名前空間:JavaScriptにおけるグローバル汚染を避けるために、私たちはモジュール化された方法で関数作用領域を利用して名前空間を構築する.
  • 多重化可能性:貼り付けは簡単ですが、その後のメンテナンスと反復を考慮して、かなり崩壊します.
  • モジュール化されたソリューションは何がありますか?
    JavaScriptモジュール化のメリットを説明しました.JavaScriptのモジュール化を実現するためにどのような解決策がありますか?
    モジュールモードを開示する(Revealing Module)
    var myRevealingModule = (function () {
        var privateVar = "Ben Cherry",
            publicVar = "Hey there!";
            
        function privateFunction() {
            console.log( "Name:" + privateVar );
        }
        function publicSetName( strName ) {
            privateVar = strName;
        }
        function publicGetName() {
            privateFunction();
        }
        // Reveal public pointers to
        // private functions and properties
        return {
            setName: publicSetName,
            greeting: publicVar,
            getName: publicGetName
        };
    })();
    myRevealingModule.setName( "Paul Kinlan" );
    この構造によって,関数を使うことによって,自分の役割領域または「クローズド」が得られた.
    この方法の利点は、名前のグローバル変数を予期せず関数の内部で使用することができますが、それでもグローバル変数にアクセスすることができます.
    利点:
  • はどこでも実現できます.
  • は、単一のファイルに複数のモジュールを定義することができる.
  • 短所:
  • は、evalを使用しない限り、プログラムでモジュールを導入できません.
  • は、依存関係を手動で処理する必要がある.
  • は、モジュールを非同期的にロードすることができません.
  • サイクル依存は面倒くさいかもしれません.
  • は、静的コード解析器による分析が難しい.
  • Common JS
    Common JSは、サーバー端末JavaScriptアプリケーションの開発を支援する一連の仕様を定義するためのプロジェクトです.CommonJSチームが解決しようとしている分野の一つがモジュールです.Node.js開発者は最初にCommunJS規格に従うつもりでしたが、その後反対することにしました.
    Common JSの仕様では、JavaScriptファイルごとに独立したモジュールコンテキスト(module context)があり、このコンテキストでデフォルトで作成した属性はすべてプライベートです.つまり、1つのファイルで定義されている変数(関数やクラスも含む)は、すべてプライベートです.他のファイルには見えません.
    注意すべき点は、CommunJSがサーバー優先でモジュールを同期させてロードすることです.もし3つのモジュールを導入すれば、彼らは一つ一つロードされます.
    // In circle.js
    const PI = Math.PI;
    exports.area = (r) => PI * r * r;
    exports.circumference = (r) => 2 * PI * r;
    // In some file
    const circle = require('./circle.js');
    console.log( `The area of a circle of radius 4 is ${circle.area(4)}`);
    Node.jsのモジュールシステムは、Library方式でCommon JSをモジュール化して実現しました.
    NodeとCommon JSのモジュールには、基本的にモジュールシステムと対話する二つのキーワードがあります.
    requireは、インターフェースを別のモジュールから現在の範囲に導入するための関数です.渡されるパラメータrequireはモジュールのidです.Nodeの実現の中で、それはnode_です.modulesディレクトリ内のモジュールの名前(または、このディレクトリ内にない場合はそのパス).
    exportsは特殊な対象です.何でも入れたら公共要素としてエクスポートされます.
    NodeとCommon JSの独特な違いはmodule.exportオブジェクトの形にあります.
    Nodeでは、module.exportsは導出された本当に特殊なオブジェクトであり、exportsはデフォルトで結合された変数module.exportsだけである.
    一方、Common JSにはmodule.exportsの対象がありません.実際の意味では、Nodeでは、完全に事前に構築されたオブジェクトmodule.exportは、以下のように導出できない:
    // This won't work, replacing exports entirely breaks the binding to
    // modules.exports.
    exports = (width) => {
        return {
            area: () => width * width
        };
    }
    // This works as expected.
    module.exports = (width) => {
        return {
            area: () => width * width
        };
    }
    長所
  • は簡単です.開発者はドキュメントを見ないで概念を把握できます.
  • は、依存管理を統合しています.モジュールは他のモジュールを必要とし、必要な順序でロードします.
  • requireは、任意の場所で呼び出すことができます.モジュールはプログラムによってロードされてもいいです.
  • は循環依存性をサポートする.
  • 欠点
  • 同期APIは、いくつかの用途(クライアント)に適合しないようにする.
  • 各モジュールに一つのファイルがあります.
  • ブラウザは、ライブラリまたは変換をロードする必要があります.
  • モジュールには構造関数(Nodeサポート)がありません.
  • は、静的コード解析が困難である.
  • AMD
    AMDは、CommunJSの研究方向に不満を持つ開発者のグループから生まれました.実際、AMDは開発の初期からCommunJSと袂を分かつことになりました.AMDとCommunJSの違いは、非同期モジュールのローディングをサポートすることにあります.
    //Calling define with a dependency array and a factory function
    define(['dep1', 'dep2'], function (dep1, dep2) {
        //Define the module value by returning a value.
        return function () {};
    });
    // Or:
    define(function (require) {
        var dep1 = require('dep1'),
        dep2 = require('dep2');
        return function () {};
    });
    JavaScriptの伝統的なクローズドを使用することによって、非同期ローディングを実現する.
    要求されたモジュールのロードが完了したときに関数を呼び出します.モジュール定義と導入モジュールは同じ関数で積載されます.モジュールを定義するときの依存関係は明確です.したがって、AMDキャリアは、動作時にプロジェクトのモジュール依存図を有することができる.したがって、互いに依存しないライブラリを同時に読み込むことができる.これはブラウザにとって特に重要です.起動時間は良好なユーザー体験にとって極めて重要です.
    長所
  • 非同期ローディング(より良い起動時間).
  • は循環依存性をサポートする.
  • requireとの互換性exports.
  • は依存管理を完全に統合している.
  • が必要であれば、モジュールを複数のファイルに分割することができる.
  • はコンストラクタをサポートします.
  • プラグインサポート(カスタムローディングステップ).
  • 欠点
  • 文法はちょっと複雑です.
  • は、コンパイルしない限り、ライブラリをロードする必要があります.
  • は、静的コードの解析が難しい.
  • 非同期ローディング以外にも、AMDのもう一つの利点は、モジュール内でオブジェクト、関数、コンストラクション、文字列、JSONまたは他のデータタイプを使用することができますが、CommunJSはオブジェクトのみをサポートします.
    UMD
    統一モジュール定義(UMD:Universal Module Definition)は、AMDとCommunJSを合わせて行う試みであり、よくあるやり方はCommunJS文法をAMD対応コードにくるむことである.
    (function(define) {
        define(function () {
            return {
                sayHello: function () {
                    console.log('hello');
                }
            };
        });
    }(
        typeof module === 'object' && module.exports && typeof define !== 'function' ?
        function (factory) { module.exports = factory(); } :
        define
    ));
    このモードの核心思想は、環境に応じて必要なパラメータの種類を判断するIIIFreamation Invoked Function Expressionというものである.
    ES 6モジュール
    JavaScript標準化をサポートするECMAチームはモジュール問題を解決し、同期と非同期の操作モードを両立させることを決定します.
    //------ lib.js ------
    export const sqrt = Math.sqrt;
    export function square(x) {
        return x * x;
    }
    export function diag(x, y) {
        return sqrt(square(x) + square(y));
    }
    //------ main.js ------
    import { square, diag } from 'lib';
    console.log(square(11)); // 121
    console.log(diag(4, 3)); // 5
    ES 6モジュールの設計思想はできるだけ静的になり、コンパイル時にモジュールの依存関係や入出力の変数が確定できるようになります.Common JSとAMDモジュールは、これらのものを実行時にのみ確認できます.
    ES 6モジュールはコンパイル時にロードされるので、静的解析が可能になります.それがあれば、JavaScriptの文法をさらに広げることができます.例えば、マクロやタイプ検査を導入するなど、静的な分析でしか実現できない機能です.静的な負荷による様々な利点の他に、ES 6モジュールには以下のような利点がある.
  • はUMDモジュールフォーマットが不要になりました.将来はサーバとブラウザはES 6モジュールフォーマットをサポートします.現在、各種の工具庫を通じて、実はすでにこの点をやり遂げました.
  • ブラウザの新しいAPIは、もはやグローバル変数またはnavigatorオブジェクトの属性を作成しなくても、モジュール形式で提供できる.
  • は、もはやオブジェクトを名前空間(例えば、Mathオブジェクト)として必要とせず、これらの機能は、将来モジュールを介して提供され得る.
  • ES 6のモジュールは自動的に厳格なモードを採用しています.モジュールの頭に「use strict」があるかどうかに関わらず. 
    厳格なモードは主に以下の制限があります.
    変数は宣言した後、関数を使用するパラメータに同名の属性があってはいけません.さもなければ、エラーメッセージはwith文を使用して読み取り専用属性に値を付けられません.さもなければ、エラーを報告するときは、プレフィクス0を使用して8進数を表してはいけません.さもなければ、エラーを報告しても削除できません.変数delete global[prop]を削除します.evalは、その外層作用領域に変数evalとargmentsを導入することはできません.再割り当てされたargmentsは、関数パラメータの変化を自動的に反映しません.argments.caleeを使用することはできません.argmentsは、argmentsを使用することができません.
    その中でも特に注意が必要です.ES 6モジュールのうち、トップレベルのthisはundefinedを指し、トップレベルのコードでthisを使用するべきではない.
    export
    export文法はJavaScriptモジュールを作成するために使われます.これを使ってオブジェクト(関数を含む)と元の値(prmitive values)を導き出すことができます.エクスポートには2つのタイプがあります.namedとdefault.
    // named
    // lib.js
    export function sum(a, b) {
        return a + b;
    }
    export function substract(a, b) {
        return a - b;
    }
    function divide(a, b) {
        return a / b;
    }
    export { divide };
    // default
    // dog.js
    export default class Dog {
        bark() {
        console.log('bark!');
        }
    }
    import
    import文は他のモジュールを導入するために使用されます.
    全体インポート
    // index.js 
    import * as lib from './lib.js'; console.log(lib.sum(1,2)); console.log(lib.substract(3,1)); console.log(lib.divide(6,3));
    一つ以上のnamedを導入してエクスポートします.
    // index.js
    import { sum, substract, divide } from './lib';
    console.log(sum(1,2));
    console.log(substract(3,1));
    console.log(divide(6,3));
    注意したいのですが、インポートとエクスポートの名前は一致していなければなりません.
    defaultをインポートしてエクスポートします.
    // index.js 
    import Dog from './dog.js'; 
    const dog = new Dog(); 
    dog.bark(); // 'bark!'
    注意、defualtは導入時に任意の名前でエクスポートできます.だから私たちはこうしてもいいです.
    import Cat from './dog.js';
    
    const dog = new Cat();
    dog.bark(); // 'bark!'
    参照
  • 完全なJavaScriptのモジュール解説https://segmentfault.com/a/1190000012464333#articleHeader9
  • JavaScript Module Systems Shown:Common JS vs AMD vs ES 2015https://auth0.com/blog/javascript-module-systems-showdown/
  • ECMAScript 6入門http://es6.ruanyifeng.com/#docs/module