JavaScript 模塊化


JavaScript 中的模塊化

最先的基於當即執行函數,閉包的模塊化

const MountClickModule = function(){
  let num = 0;
  const handleClick = ()=>{
    console.log(++num);
  }

  return {
    countClick:()=>{
      document.addEventListener('click',handleClick)
    }
  }
}();

MountClickModule.countClick();

(function(module) {
module.say = ()=>{
  console.log(num)  //undefined
  //do something
}
})(MountClickModule);

MountClickModule.say();

這種閉包的壞處:javascript

  • 擴展模塊間沒法訪問私有變量。
  • 強依賴模塊導入的順序,項目變大後很差維護。

AMD 和 CommonJS 模塊化JavaScript 應用

AMD 和 CommonJS 是兩個互相競爭的標準,都可定義 JavaScript 模塊。除了語法和原理的區別以外,主要區別是 AMD 的設計理念是明確基於瀏覽器,而 CommonJS 的設計是面向通用 JavaScript 環境

使用 AMD 定義模塊依賴

AMD 異步模塊定義規範制定了定義模塊的規則,這樣模塊和模塊的依賴能夠被異步加載。這和瀏覽器的異步加載模塊的環境恰好適應(瀏覽器同步加載模塊會致使性能、可用性、調試和跨域訪問等問題)。html

目前,AMD 最流行的實現是 RequireJSjava

define("alpha", ["require", "exports", "beta"], function (require, exports, beta) {
       exports.verb = function() {
           return beta.verb();
           //Or:
           return require("beta").verb();
       }
   });

AMD 提供名爲 aplha 的函數,它接收一下參數:git

  • 第一個參數,id,是個字符串。它指的是定義中模塊的名字,這個參數是可選的。若是沒有提供該參數,模塊的名字應該默認爲模塊加載器請求的指定腳本的名字。
  • 第二個參數,dependencies,是個定義中模塊所依賴模塊的數組。
  • 第三個參數是初始化模塊的工廠函數,該函數接收dependencies做爲參數
    AMD異步獲取依賴,以免阻塞,若是依賴來自服務器,那麼這個過程將花費一些時間,知道依賴所有加載完成後,調用模塊的工廠函數,並傳入全部的依賴。

能夠看出,AMD 有一下幾項有點:es6

  • 異步加載模塊,避免阻塞。
  • 自動處理依賴,咱們無需考慮模塊的引入順序。
  • 在同一個文件中能夠定義多個模塊。

CMD

CMD 是 SeaJS 在推廣過程當中對模塊定義的規範化產出,在 CMD 規範中,一個模塊就是一個文件。代碼的書寫格式以下:github

define(function (require, exports, module) {
  const foo = require('./foo')
  a.doSomething()
  // ...
  const bar = require('./bar') // 依賴能夠就近書寫
  b.doSomething()
  // do something else
})

CommonJS

AMD 的設計明確基於瀏覽器,而 CommonJS 的設計是面向通用 JavaScript 環境。CommonJS 目前在 Nodejs 社區中具備最多的用戶。CommonJS 使用基於文件的模塊,因此每一個文件中都只能定義一個模塊,CommonJs 提供變量 module,該變量具備屬性 exports,經過 exports 很容易擴展屬性。最後,module.exports 做爲模塊的公共接口。typescript

const beta = require('beta');
function alpha(){
    return beta.verb();
    //Or:
    return require("beta").verb();
}

module.exports = alpha;

CommonJS 要求一個文件就是一個模塊,文件中的代碼就是模塊的一部分,因此不須要使用當即執行函數來包裝變量,在模塊中定義的變量都是安全的再模塊中,不會泄露到全局做用域。只有經過 module.exports 對象暴露的對象或函數才能夠在函數外部訪問
CommonJS 具備如下特色:api

  • 基於文件系統。
  • 引入模塊時文件同步加載,能夠訪問模塊的公共接口。
  • 模塊加載相對更快

這是 CommonJS 在服務端更流行的緣由。跨域

ES6 模塊的導入導出

ES6 模塊結合了CommonJS 和 AMD 的有點,具體以下:數組

  • 與 CommonJS 相似,ES6模塊語法相對簡單,而且基於文件(每個文件就是一個模塊)
  • 與 AMD 相似,ES6 模塊支持異步加載模塊。

既 ES6 結合了兩種模塊化的有點,基於文件系統,既支持異步也支持同步,由於瀏覽器並無實現 ES6 的模塊化 API 因此具體是異步仍是同步取決於loader api

ES6 模塊的主要思想是必須顯示的使用標誌符導出模塊,才能從外部訪問模塊。其餘標誌符,甚至在最頂級做用域中定義的標識符,只能在模塊中使用。
ES6 引入兩個關鍵字:

  • export ---- 從模塊外部指定標識符。
  • import ---- 導入模塊標識符。

從index.js模塊中導出:

const hello = 'hello';
  export const name = 'yunfly'

  export function sayHi(){
    return `${hello} ${name}!`
  }

也能夠在模塊最後一塊兒導出:

// foo.js
  const hello = 'hello';
  export const name = 'yunfly'

  export function sayHi(){
    return `${hello} ${name}!`
  }

  export { name, sayHi }

  // export { name as firstName, sayHi }
  

  // bar.js

  // 使用 as 設置導如別名
  import { name as firstName, sayHi } from 'foo'

  console.log(name)
  sayHi()

  //bar2.js
  // 導出所有標識符:
  import * as sayModule from 'foo';

  console.log(sayModule.name)
  sayModule.sayHi()

默認導出 export default 被認爲是有害的

// foo.js
class Foo {}

export default Foo


// bar.js
import Foo from './foo';

這存在一些可維護性的問題:

  • 若是你在 foo.ts 裏重構 Foo,在 bar.ts 文件中,它將不會被從新命名;
  • 若是你最終須要從 foo.ts 文件中導出更多有用的信息(在你的不少文件中都存在這種情景),那麼你必須兼顧導入語法。
  • 在 ts 中默認導出的可發現性很是差,你不能智能的辨別一個模塊它是否有默認導出。

文章參考:《JavaScript忍者祕籍》

相關文章
相關標籤/搜索