JavaScriptモジュール化原理の浅分析

11072 ワード

前に書く
モジュール化とは、複雑なシステムを複数のモジュールに分解して符号化を容易にすることをいう.JSモジュール化の大まかな流れは、CommunJS(サーバー)->AMD(ブラウザ側)->UMD(CommonJSとAMDに対応)->ES Module(ES 6規格)です.本論文ではそれらの用法から紹介し、その原理を簡単に実現する.モジュール化パッキングツールwebpackを簡単に実現します.
本稿は以下の部分から総括する.
  • Common JSの使い方と原理
  • AMDの使い方と原理
  • ES 2015標準化
  • 自動化構築
  • 簡易実装webpack
  • Common JS
    CommmonJSは広範なJavaScriptモジュール化規範を使用しており、コア思想はrequire方法によって同時に依存する他のモジュールをロードし、module.exportを通じて暴露が必要なインターフェースを導き出す.
    Common JSの使い方
    Common JSを採用して導入したコードの使い方は以下の通りです.
    //  require    
    const someFun= require('./moduleA');
    someFun();
    
    //  module.exports  
    module.exports = someFunc;
    
    Common JSの原理
    a.js:
    console.log('aaa');
    exports.name = '  a     ';
    
    b.js:
    let fs = require('fs');
    let path = require('path');
    let b = req('./a.js');
    // req  CommonJS  require  
    function req(mod) {
        let filename = path.join(__dirname, mod);
        let content = fs.readFileSync(filename, 'utf8');
        /**
           *              ,       
           *function fn(exports, module, require, __dirname, __filename) {
           *  module.exports = '             '
           *  return module.exports
           *}
        */
        let fn = new Function('exports', 'require', 'module', '__filename', '__dirname', content + '
    return module.exports;'
    ); let module = { exports: {} }; return fn(module.exports, req, module, __filename, __dirname); }
    AMD
    AMDはまた、JavaScriptモジュール化の仕様であり、CommunJSと最大の違いは、非同期的に依存するモジュールをロードすることである.AMD仕様は主にブラウザ環境に対するモジュール化問題を解決するために、最も代表的な実現はrequirejsです.
    AMDのメリット:
  • は、コードを変換せずにブラウザ内で実行することができる
  • .
  • は、複数の依存性
  • をロードすることができる.
  • コードは、ブラウザ環境とノード.js環境下で実行できます.
    AMDの欠点:
  • JavaScript運行環境にはAMDをサポートしていません.AMDを実現したライブラリを導入してから正常に使用できます.
  • //   define        
    define('a', [], function () {
        return 'a';
    });
    define('b', ['a'], function (a) {
        return a + 'b';
    });
    //   require      
    require(['b'], function (b) {
        console.log(b);
    });
    
    AMDの原理
    let factories = {};
    /**
     *   AMD define  
     * @param moduleName      
     * @param dependencies   
     * @param factory     
    */
    function define(modName, dependencies, factory) {
        factory.dependencies = dependencies;
        factories[modName] = factory;
    }
    /**
     *   AMD require  
     * @param mods      
     * @param callback     
     */
    function require(modNames, callback) {
        let loadedModNames = modNames.map(function (modName) {
            let factory = factories[modName];
            let dependencies = factory.dependencies;
            let exports;
            require(dependencies, function (...dependencyMods) {
                exports = factory.apply(null, dependencyMods);
            });
            return exports;
        })
        callback.apply(null, loadedModNames);
    }
    
    ES 2015モジュール化
    ES 2015モジュール化は、ECMAが提案したJavaScriptモジュール化仕様であり、言語レベルでモジュール化されている.ブラウザメーカーとNode.jsは、この規格を原生でサポートすると発表しました.これは徐々にCommonJSとAMD仕様を交換し、ブラウザとサーバー共通のモジュールソリューションになります.ES 2015をモジュール化して導入およびエクスポートする際のコードは以下の通りです.
    //   import  
    import { name } from './person.js';
    
    //   export  
    export const name = 'musion';
    
    ES 2015モジュールは究極のモジュール化方式であるが、ほとんどのJavaScriptが実行されていない現在のところ、ツールを構築して標準のES 5に変換してから正常に動作するという欠点がある.
    自動化構築
    自動化構築とは、このことを行います.ソースコードをオンラインに発行されるJavaScrip、CSS、HTMLコードに変換して、下記の内容を含みます.
  • コード変換:ECMASCRIPT 6はECMASCRIPT 5、LESSはCSSなどにコンパイルされます.
  • ファイル最適化:JavaScript、CSS、HTMLコードを圧縮し、合併画像などを圧縮する.
  • コード分割:複数ページの共通コードを抽出し、画面の実行部分を抽出するコードを非同期的にロードする必要がない.
  • モジュールの合併:モジュール化されたプロジェクトを採用するには多くのモジュールとファイルがあります.モジュールを一つのファイルに分類する機能を構築する必要があります.
  • 自動更新:ローカルソースコードの変化をモニターし、ブラウザを自動再構築、更新する.
  • コードチェック:コードが倉庫に提出される前にコードが仕様に合っているかどうかを確認し、ユニットテストが合格したかを確認する必要があります.
  • は自動的に発行します.コードを更新したら、自動的にライン上でコードを発行し、リリースシステムに送信します.
  • webpack
    webpackはパッキングモジュール化JavaScriptのツールで、webpackの中ですべてのファイルはモジュールで、Loaderを通じてファイルを変換して、Pluginを通じてフックに注入して、最後に複数のモジュールで結合されたファイルを出力します.Webpackはモジュール化プロジェクトの構築に専念します.
    すべてのファイル:JavaScript、CSS、SCSS、画像、テンプレートは、Webpackの目にはモジュールが一つずつあります.このような利点は、各モジュール間の依存関係を明確に記述でき、Webpackがモジュールを組み合わせて包装するのに便利です.Webpackの処理を経て、最終的にブラウザで使える静的な資源を出力します.
    webpackの簡単な掲示
    webpackでパッケージ構築する時、パッケージを確認しますか?後のファイルは、余ったコードを削除し、残りのコアコードは以下の通りです.
    (function (modules) {
        function require(moduleId) {
            var module = {
                exports: {}
            };
            // moduleId ->      
            modules[moduleId].call(module.exports, module, module.exports, require);
            return module.exports;
    
        }
        return require("./index.js");
    }) ({
        "./index.js":
        (function (module, exports, require) {
            eval("console.log('hello');

    "
    ); }) });
    簡単にwebpackを実現します.
    #! /usr/bin/env node
    //             
    const pathLib = require('path');
    const fs = require('fs');
    let ejs = require('ejs');
    let cwd = process.cwd();
    let { entry, output: { filename, path } } = require(pathLib.join(cwd, './webpack.config.js'));
    let script = fs.readFileSync(entry, 'utf8');
    let bundle = `
    (function (modules) {
        function require(moduleId) {
            var module = {
                exports: {}
            };
            modules[moduleId].call(module.exports, module, module.exports, require);
            return module.exports;
    
        }
        return require("");
    })
        ({
            "":
                (function (module, exports, require) {
                    eval("");
                })
        });
    `
    let bundlejs = ejs.render(bundle, {
        entry,
        script
    });
    try {
        fs.writeFileSync(pathLib.join(path, filename), bundlejs);
    } catch (e) {
        console.error('     ', e);
    }
    console.log('compile sucessfully!');
    
    他のモジュールに依存する場合
    #! /usr/bin/env node
    //             
    const pathLib = require('path');
    const fs = require('fs');
    let ejs = require('ejs');
    let cwd = process.cwd();
    let { entry, output: { filename, path } } = require(pathLib.join(cwd, './webpack.config.js'));
    let script = fs.readFileSync(entry, 'utf8');
    let modules = [];
    script.replace(/require\(['"](.+?)['"]\)/g, function () {
        let name = arguments[1];
        let script = fs.readFileSync(name, 'utf8');
        modules.push({
            name,
            script
        });
    });
    let bundle = `
    (function (modules) {
        function require(moduleId) {
            var module = {
                exports: {}
            };
            modules[moduleId].call(module.exports, module, module.exports, require);
            return module.exports;
        }
        return require("");
    })
        ({
            "":
                (function (module, exports, require) {
                    eval(\`\`);
                })
           0){%>,
               
                "":
                (function (module, exports, require) {
                    eval(\`\`);
                })
                
        });
    `
    let bundlejs = ejs.render(bundle, {
        entry,
        script,
        modules
    });
    try {
        fs.writeFileSync(pathLib.join(path, filename), bundlejs);
    } catch (e) {
        console.error('     ', e);
    }
    console.log('compile sucessfully!');
    
    参考資料
    精読jsモジュール化発展
    チョモランマ先端架設師コース
    補足読み
    JavaScriptモジュール化七日間談
    JavaScriptモジュール化プログラミング簡略史(2009-2016)