JavaScript模塊化規範詳解

JavaScript模塊化規範詳解

目錄

  1. 爲何要模塊化javascript

  2. 模塊化的好處css

  3. 頁面引入加載script存在的問題html

  4. 模塊化規範java


爲何要模塊化?

  1. Web sites are turning into Web Apps.

  2. Code complexity(複雜度) grows as the site gets bigger.

  3. Highly decoupled(解耦) JS files/modules is wanted.

  4. Deployment(部署) wants optimized(優化) code in few HTTP calls.

模塊化的好處

  1. 避免命名衝突(減小命名空間污染)

  2. 更好的分離,按需加載

  3. 更高複用性

  4. 高可維護性

頁面引入加載script存在的問題:

  • 請求過多

  • 依賴模糊

  • 難以維護

模塊化規範

CommonJS

每一個文件均可當作一個模塊

  • 在服務器端: 模塊的加載是運行時同步加載的。

  • 在瀏覽器端: 模塊須要提早編譯打包處理。

基本語法:

  • 暴露模塊:
    • module.exports = value
    • exports.xxx = value
  • 引入模塊:
    • require(xxx)
    • 第三方模塊: xxx爲模塊名
    • 自定義模塊: xxx爲模塊文件路徑

實現:

  • 服務器端實現: Node.js
  • 瀏覽器端實現: Browserify,也稱爲CommonJS的瀏覽器端的打包工具。

Node.js模塊化過程

  1. 安裝Node.js

  2. 建立項目結構

    |-modules
      |-module1.js
      |-module2.js
      |-module3.js
    |-app.js
    |-package.json
      {
        "name": "commonJS-node",
        "version": "1.0.0"
      }
  3. 下載第三方模塊
    • npm install uniq --save
  4. 模塊化編碼
    • module1.js

      module.exports = {
          foo() {
              console.log('module1 foo()');
          }
      };
    • module2.js

      module.exports = function() {
          console.log('module2()');
      };
    • module3.js

      exports.foo = function() {
          console.log('module3 foo()');
      };
      
      exports.bar = function() {
          console.log('module3 bar()');
      };
    • app.js

      /*
       * 1. 定義暴露模塊:
       *  module.exports = value;
       *  exports.xxx = value;
       * 2. 引入模塊:
       *  var module = require(模塊名或模塊路徑);
       */
      
      // 引用模塊
      let fs = require('fs'); // fs是nodejs中內置的文件系統模塊
      let uniq = require('uniq'); // 下載的第三方模塊,功能是數組排序去重
      let module1 = require('./modules/module1'); // 自定義的module
      let module2 = require('./modules/module2');
      let module3 = require('./modules/module3');
      
      // 使用模塊
      module1.foo();
      module2();
      
      console.log(uniq([1, 3, 2, 4, 2]));
      
      fs.readFile('app.js', function(error, data){
          console.log(data.toString());
      })
  5. 經過node運行app.js: node app.js


Browserify模塊化過程

因爲瀏覽器端不具有node那樣的環境,不能識別require等方法,因此瀏覽器端的模塊化須要藉助Browserify工具來完成打包,以便瀏覽器識別。

  1. 建立項目結構

    |-js
      |-dist // 打包生成文件的目錄
      |-src // 源碼所在的目錄
        |-module1.js
        |-module2.js
        |-module3.js
        |-app.js // 應用主源文件
    |-index.html
    |-package.json
      {
        "name": "browserify-test",
        "version": "1.0.0"
      }
  2. 下載browserify

    • 全局: npm install browserify -g // 全局環境安裝
    • 局部: npm install browserify --save-dev // 只是幫助咱們編譯打包文件,在開發環境(-dev)下安裝便可,未來生產環境並不須要

    安裝完後,package.json中會變成:
    { "name": "browserify-test", "version": "1.0.0" "devDependencies": {"browserify": 版本號}, // 開發環境下的依賴包 "dependencies": {"uniq": 版本號} // 全局依賴包 }

  3. 定義模塊代碼

    • 和Node.js中同樣的定義。
  4. 打包處理js源文件:

    • browserify js/src/app.js -o js/dist/build.js

    • 上面的命令執行完畢後,生成了打包後的文件,就能夠在html頁面中引入了:

      <script type="text/javascript" src="js/dist/build.js"></script>


AMD

Asynchronous Module Definition(異步模塊定義)

專門用於瀏覽器端,模塊的加載是異步的。

語法:

  1. 定義暴露模塊

    • 定義沒有依賴的模塊

      define(function(){
          return 模塊
      })
    • 定義有依賴的模塊

      define(['module1','module2'], function(m1, m2){
          return 模塊
      })
  2. 引入使用模塊

    require(['module1','module2'], function(m1, m2){ //顯式聲明依賴注入
        使用m1/m2
    })

實現: Require.js

在沒有使用AMD規範(require.js)的時候,咱們經過多個script標籤來按照依賴順序依次引入js文件,
這樣不但增長了HTTP請求數,更增長了維護的難度,容易出錯。

經過模塊加載器require.js來加載js模塊:

  1. 下載Require.JS,官網: http://www.requirejs.cn/

  2. 建立項目結構

    |-js
      |-libs
        |-require.js
      |-modules
        |-module1.js
        |-module2.js
      |-main.js
    |-index.html
  3. 定義模塊代碼

    • module1.js

      define(function(){
          let msg = 'hello';
          function getMsg(){
              return msg.toUpperCase();
          }
          return {getMsg};
      });
    • module2.js

      define(['module1', 'jquery'], function(m1, $){
          let name = "module2";
          function showMsg(){
              $('body').css('background', "red");
              alert(m1.getMsg() + ',' + name);
          }
          return {showMsg};
      });
  4. 編寫應用主入口: main.js

    (function () {
        // 配置
        require.config({
            //基本路徑
            baseUrl: "js/",
            //模塊標識名與模塊路徑映射
            paths: {
                "module1": "./modules/module1", // 內部會給路徑自動加上.js擴展名
                "module2": "./modules/module2",
            }
        });
    
        // 引入使用模塊
        require(['module2'], function(module2){
            module2.showMsg();
        })
    })()
  5. 頁面使用模塊

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

    require.js 在加載的時候會檢查 data-main 屬性:
    能夠在data-main指向的腳本中設置模板加載 選項,而後加載第一個應用模塊。

    注意:你在main.js中所設置的腳本是異步加載的。因此若是你在頁面中配置了其它JS加載,則不能保證它們所依賴的JS已經加載成功

    例如:

    <script data-main="scripts/main" src="scripts/require.js"></script>
    <script src="scripts/other.js"></script>
    // contents of main.js:
    require.config({
        paths: {
            foo: 'libs/foo-1.1.3'
        }
    });
    // contents of other.js:
    
    // This code might be called before the require.config() in main.js
    // has executed. When that happens, require.js will attempt to
    // load 'scripts/foo.js' instead of 'scripts/libs/foo-1.1.3.js'
    require( ['foo'], function( foo ) {
    
    });
  6. 使用第三方基於require.js的框架(jquery)

    jQuery支持AMD規範,在源碼的最後幾行,define("jquery", [], function(){return jQuery;}); 這說明jQuery暴露了一個模塊接口,而且標識名爲jquery。

    注意在引入的時候,寫jquery而不是jQuery

    將jQuery庫文件導入到項目的libs目錄中,而後在main.js中配置jquery路徑:

    path: {
        'jquery': './libs/jquery-1.10.1'
    }

    接下來就可使用在module中了。

    define(['module1', 'jquery'], function (module1, $) {
        var name = 'Tom';
        function showMsg() {
            $('body').css({background : 'red'})
            alert(name + ' ' + module1.getMsg())
        }
        return {showMsg}
    })
  7. 使用第三方不基於require.js的框架(angular)

    將angular.js/angular-messages.js導入項目目錄,而後在paths中添加angular路徑。

    爲了配置不兼容AMD的模塊,須要在require.config中多添加:

    shim: {
        'angular': {
            exports: 'angular'
        },
        `angular-messages`: {
            exports: 'angular-message',
            deps: ['angular']
        }
    }

CMD

專門應用於瀏覽器端,模塊的加載是異步的。

實現: sea.js,github: https://github.com/seajs/seajs

模塊使用時纔會加載執行。

語法:

  1. 定義暴露模塊

    • 定義沒有依賴的模塊

      define(function(require, exports, module){
          exports.xxx = value;
          module.exports = value;
      })
    • 定義有依賴的模塊

      define(function(require, exports, module){
          //引入依賴模塊(同步)
          var module2 = require('./module2');
          //引入依賴模塊(異步),注意異步的function會在主線程執行完畢再執行,所以輸出順序可能變化
          require.async('./module3', function(m3){
          })
          //暴露模塊
          exports.xxx = value;
      })
  2. 引入使用模塊

    define(function(require){
        var m1 = require('./module1');
        var m4 = require('./module4');
        m1.show();
        m4.show();
    })

CMD規範,定義模塊相似AMD,暴露模塊相似Commonjs。

使用方法:

  1. 下載sea.js並引入到libs。

  2. 建立項目結構

    |-js
      |-libs
        |-sea.js
      |-modules
        |-module1.js
        |-module2.js
        |-module3.js
        |-module4.js
        |-main.js
    |-index.html
  3. 定義sea.js的模塊代碼

    • module1.js

      define(function(require, exports, module) {
          var data = "hello";
          function show(){
              console.log('module1 show()' + data);
          }
          // 向外暴露
          exports.show = show;
      })
    • 主入口模塊: main.js

      define(function(require){
          var m1 = require('./module1');
          m1.show();
      })
  4. 在index頁面引入

    <script type="text/javascript" src="js/libs/sea.js"></script>
    <script type="text/javascript">
        seajs.use('./js/modules/main.js')
    </script>

ES6模塊化規範

ES6中內置了js模塊化的實現。

語法:

  1. 定義暴露模塊: export

    • 暴露一個對象(默認暴露):

      export default 對象

      能夠暴露任意數據類型,暴露什麼就接收到什麼。

    • 暴露多個對象(常規暴露):

      // 分別暴露
      export var xxx = value1;
      export let yyy = value2;
      
      // 統一暴露
      var xxx = value1;
      let yyy = value2;
      export {xxx, yyy}

      統一暴露或者分別暴露,在引入的時候必須用對象解構賦值的形式。

  2. 引入使用模塊: import

    • 默認暴露的模塊:

      import xxx from '模塊路徑/模塊名'
    • 其餘模塊

      import {xxx, yyy} from '模塊路徑/模塊名'
      
      import * as module1 from '模塊路徑/模塊名'

問題:

一些瀏覽器還不能直接識別ES6模塊化的的語法。

須要使用Babel來將ES6轉換爲ES5,但因爲內部還使用了CommonJS,因此瀏覽器仍然不能直接執行。

接着再次使用Browserify來將文件打包處理,最終引入頁面,瀏覽器能夠直接運行。

js轉換及打包方法:

  1. 定義package.json文件

    {
        "name": "es6_babel_browserify-test",
        "version": "1.0.0"
    }
  2. 安裝babel-cli, babel-preset-es2015和browserify

    • npm install babel-cli browserify -g
    • npm install babel-preset-es2015 --save-dev
  3. 定義.babelrc配置文件(babel在執行以前會先讀取該文件)

    {
        "presets": ["es2015"] // 該命令決定了babel要去執行的任務,"es2015"表示ES6語法轉換
    }
  4. 編寫模塊

    • js/src/module1.js

      something...

    • js/src/app.js

      import {fun1, fun2} from './module1';
      import $ from 'jquery';
      
      $('body').css('background', 'red');
      fun1();
      fun2();
  5. 編譯打包

    • 使用Babel編譯爲ES5語法(包含CommonJS):

      babel js/src -d js/build (能夠自動生成新目錄build)

    • 使用Browserify打包js:

      browserify js/build/app.js -o js/dist/bundle.js(不能自動生成dist目錄)

  6. 頁面中引入

    <script type="text/javascript" src="js/build/bundle.js"></script>

  7. 引入第三方模塊(jQuery)

    1. 下載jQuery模塊:

      npm install jquery@1 --save (模塊後加@表明下載相應版本號下的最新版本)

    2. 在app.js中引入使用:

      import $ from 'jquery'

注意:

當改變了模塊中的代碼後,須要從新轉換(Babel)、編譯打包(Browserify),再引入頁面。

相關文章
相關標籤/搜索