JS前端模塊化規範

 
  • 在開發中在導入模塊時常用requireimport
  • 導出模塊時使用module.exports/exports或者export/export default;
  • 有時候爲了引用一個模塊會使用require奇怪的是也可使用import????它們之間有何區別呢?
因而有了菜鳥解惑的搜嘍過程。。。。。。

追溯根源,來到Js模塊化規範

一、CommonJS規範(同步加載模塊)

  • 容許模塊經過require方法來同步加載所要依賴的其餘模塊,而後經過exports或module.exports來導出須要暴露的接口。
  • 使用方式:
// 導入 require("module"); require("../app.js"); // 導出 exports.getStoreInfo = function() {}; module.exports = someValue;
    • 優勢:javascript

      1. 簡單容易使用
      2. 服務器端模塊便於複用
    • 缺點:java

      1. 同步加載方式不適合在瀏覽器環境中使用,同步意味着阻塞加載,瀏覽器資源是異步加載的
      2. 不能非阻塞的並行加載多個模塊
    • 爲何瀏覽器不能使用同步加載,服務端能夠?node

      • 由於模塊都放在服務器端,對於服務端來講模塊加載時
      • 而對於瀏覽器端,由於模塊都放在服務器端,加載的時間還取決於網速的快慢等因素,若是須要等很長時間,整個應用就會被阻塞。
      • 所以,瀏覽器端的模塊,不能採用"同步加載"(CommonJs),只能採用"異步加載"(AMD)。
  • 參照CommonJs模塊表明node.js的模塊系統

AMD(異步加載模塊)

  • 採用異步方式加載模塊,模塊的加載不影響後面語句的運行。全部依賴模塊的語句,都定義在一個回調函數中,等到加載完成以後,回調函數才執行。
  • 使用實例:
// 定義 define("module", ["dep1", "dep2"], function(d1, d2) {...}); // 加載模塊 require(["module", "../app"], function(module, app) {...}); 
  • 加載模塊require([module], callback);第一個參數[module],是一個數組,裏面的成員就是要加載的模塊;第二個參數callback是加載成功以後的回調函。
  • 優勢:react

    1. 適合在瀏覽器環境中異步加載模塊
    2. 能夠並行加載多個模塊
  • 缺點:typescript

    1. 提升了開發成本,代碼的閱讀和書寫比較困難,模塊定義方式的語義不暢
    2. 不符合通用的模塊化思惟方式,是一種妥協的實現
  • 實現AMD規範表明require.js
RequireJS對模塊的態度是預執行。因爲 RequireJS 是執行的 AMD 規範, 所以全部的依賴模塊都是先執行;即RequireJS是預先把依賴的模塊執行,至關於把require提早了
  • RequireJS執行流程:
  1. require函數檢查依賴的模塊,根據配置文件,獲取js文件的實際路徑
  2. 根據js文件實際路徑,在dom中插入script節點,並綁定onload事件來獲取該模塊加載完成的通知。
  3. 依賴script所有加載完成後,調用回調函數

CMD規範(異步加載模塊)

  • CMD規範和AMD很類似,簡單,並與CommonJS和Node.js的 Modules 規範保持了很大的兼容性;在CMD規範中,一個模塊就是一個文件。
  • 定義模塊使用全局函數define,其接收 factory 參數,factory 能夠是一個函數,也能夠是一個對象或字符串;
  • factory 是一個函數,有三個參數,function(require, exports, module):數組

    1. require 是一個方法,接受模塊標識做爲惟一參數,用來獲取其餘模塊提供的接口:require(id)
    2. exports 是一個對象,用來向外提供模塊接口
    3. module 是一個對象,上面存儲了與當前模塊相關聯的一些屬性和方法
  • 實例:
define(function(require, exports, module) { var a = require('./a'); a.doSomething(); // 依賴就近書寫,何時用到何時引入 var b = require('./b'); b.doSomething(); });
  • 優勢:
  1. 依賴就近,延遲執行
  2. 能夠很容易在 Node.js 中運行
  • 缺點:
  1. 依賴 SPM 打包,模塊的加載邏輯偏重
  • 實現表明庫sea.js:SeaJS對模塊的態度是懶執行, SeaJS只會在真正須要使用(依賴)模塊時才執行該模塊

AMD 與 CMD 的區別

  1. 對於依賴的模塊,AMD 是提早執行,CMD 是延遲執行。不過 RequireJS 從2.0開始,也改爲了能夠延遲執行(根據寫法不一樣,處理方式不一樣)。CMD 推崇 as lazy as possible.
  2. AMD推崇依賴前置;CMD推崇依賴就近,只有在用到某個模塊的時候再去require。
// AMD define(['./a', './b'], function(a, b) { // 依賴必須一開始就寫好 a.doSomething() // 此處略去 100 行 b.doSomething() ... }); // CMD define(function(require, exports, module) { var a = require('./a') a.doSomething() // 此處略去 100 行 var b = require('./b') // 依賴能夠就近書寫 b.doSomething() // ... });

UMD

  • UMD是AMD和CommonJS的糅合
  • AMD 以瀏覽器第一原則發展異步加載模塊。
  • CommonJS 模塊以服務器第一原則發展,選擇同步加載,它的模塊無需包裝。
  • UMD先判斷是否支持Node.js的模塊(exports)是否存在,存在則使用Node.js模塊模式;在判斷是否支持AMD(define是否存在),存在則使用AMD方式加載模塊。
(function (window, factory) { if (typeof exports === 'object') { module.exports = factory(); } else if (typeof define === 'function' && define.amd) { define(factory); } else { window.eventUtil = factory(); } })(this, function () { //module ... });

ES6模塊化

  • ES6 在語言標準的層面上,實現了模塊功能,並且實現得至關簡單,徹底能夠取代 CommonJS 和 AMD 規範,成爲瀏覽器和服務器通用的模塊解決方案。
  • ES6 模塊設計思想:儘可能的靜態化、使得編譯時就能肯定模塊的依賴關係,以及輸入和輸出的變量(CommonJS和AMD模塊,都只能在運行時肯定這些東西)。瀏覽器

    • 使用方式:
// 導入 import "/app"; import React from 「react」; import { Component } from 「react」; // 導出 export function multiply() {...}; export var year = 2018; export default ... ...
  • 優勢:
  1. 容易進行靜態分析
  2. 面向將來的 EcmaScript 標準
  • 缺點:
  1. 原生瀏覽器端尚未實現該標準
  2. 全新的命令字,新版的 Node.js才支持。

回到問題「require與import的區別」

  • require使用與CommonJs規範,import使用於Es6模塊規範;因此二者的區別實質是兩種規範的區別;
  • CommonJS:
  1. 對於基本數據類型,屬於複製。即會被模塊緩存;同時,在另外一個模塊能夠對該模塊輸出的變量從新賦值。
  2. 對於複雜數據類型,屬於淺拷貝。因爲兩個模塊引用的對象指向同一個內存空間,所以對該模塊的值作修改時會影響另外一個模塊。
  3. 當使用require命令加載某個模塊時,就會運行整個模塊的代碼。
  4. 當使用require命令加載同一個模塊時,不會再執行該模塊,而是取到緩存之中的值。也就是說,CommonJS模塊不管加載多少次,都只會在第一次加載時運行一次,之後再加載,就返回第一次運行的結果,除非手動清除系統緩存。
  5. 循環加載時,屬於加載時執行。即腳本代碼在require的時候,就會所有執行。一旦出現某個模塊被"循環加載",就只輸出已經執行的部分,還未執行的部分不會輸出。
  • ES6模塊
  1. ES6模塊中的值屬於【動態只讀引用】。
  2. 對於只讀來講,即不容許修改引入變量的值,import的變量是隻讀的,不管是基本數據類型仍是複雜數據類型。當模塊遇到import命令時,就會生成一個只讀引用。等到腳本真正執行時,再根據這個只讀引用,到被加載的那個模塊裏面去取值。
  3. 對於動態來講,原始值發生變化,import加載的值也會發生變化。不管是基本數據類型仍是複雜數據類型。
  4. 循環加載時,ES6模塊是動態引用。只要兩個模塊之間存在某個引用,代碼就可以執行。
  • 最後:require/exports 是必要通用且必須的;由於事實上,目前你編寫的 import/export 最終都是編譯爲 require/exports 來執行的。
相關文章
相關標籤/搜索