1. 程式人生 > 其它 >commonjs和ES Module

commonjs和ES Module

commonjs匯出方法exports和module.exports的區別?

commonjs實際匯出的就是exports這個物件,可以把需要匯出的變數、方法等繫結在exports這個物件上匯出,例如:

//a.js
exports.name = '阿明先森'
console.log(require('./a.js') );

列印:

{
    "name": "amingxiansen"
}

小結:所以用exports匯出的都是物件,匯出內容包含在物件裡。

module.exportsexports具有相同的引用。module.exports可以直接匯出函式、變數等。例如:

module.exports  = '阿明先森'

列印:

小結:當然想要匯出物件也沒問題:module.exports = {};相比exports,更靈活也更容易理解,推薦使用module.exports進行匯出。

如果使用exports = {name: '阿明先森'}匯出物件會怎麼樣呢?
列印:

列印為空,原因是exports是匯出函式傳入的引數,exports = {}相當於在函式裡面重新聲明瞭一個exports變數,和原來的引數脫離了關係,所以打印出來的是原來的引數,為空。
舉個例子:

const myExports  = {}
function Test(myExports) {
  myExports  = {name: 'amingxiansen'} //這裡的myExports與引數脫離了關係,相當於在區域性聲明瞭let Exports
}
console.log(Test(myExports)) // {}

小結: 所以使用exports匯出,必須要把匯出內容繫結在exports上,作為它的一個屬性匯出。

module.exports ={
    name:'amingxiansen'
}
exports.name = 'alien' // 此時 `exports.name` 是無效的

此時exports會被module.exports覆蓋
小結: module.exports優先順序高於exports,應該避免兩種匯出混合使用。

commonjs實現原理?

在編譯過程中,commonjs經過了收尾的包裝,像下面這樣:

(function(exports,require,module,__filename,__dirname){
   const sayName = require('./hello.js')
    module.exports = function say(){
        return {
            name:sayName(),
            author:'我不是外星人'
        }
    }
})

Commonjs 規範模組中,會形成一個包裝函式,我們寫的程式碼將作為包裝函式的執行上下文,使用的 require ,exports ,module 本質上是通過形參的方式傳遞到包裝函式中的。

require載入原理?

每個檔案都是一個module,module上除了有模組的基本資訊之外,還會有個loaded代表模組是否已經載入過。二次載入會直接使用快取中的資料。
以nodejs為例,整個系統執行之後,會用 Module 快取每一個模組載入的資訊。
加入快取的順序是:先加入快取再執行
require原始碼:

 // id 為路徑識別符號
function require(id) {
   /* 查詢  Module 上有沒有已經載入的 js  物件*/
   const  cachedModule = Module._cache[id]
   
   /* 如果已經載入了那麼直接取走快取的 exports 物件  */
  if(cachedModule){
    return cachedModule.exports
  }
 
  /* 建立當前模組的 module  */
  const module = { exports: {} ,loaded: false , ...}

  /* 將 module 快取到  Module 的快取屬性中,路徑識別符號作為 id */  
  Module._cache[id] = module
  /* 載入檔案 */
  runInThisContext(wrapper('module.exports = "123"'))(module.exports, require, module, __filename, __dirname)
  /* 載入完成 *//
  module.loaded = true 
  /* 返回值 */
  return module.exports
}

ES module

對模組化module的支援是從ES6以後開始的。ES module匯入匯出使用 importexport。可以匯出函式、變數、物件等。
export default //預設匯出 ===> import temp from './a.js'
export {a, b, c} ===> import {a, b, c} from './a.js'
也可以混合匯入匯出。

es module的特性

  1. ES6 module 的引入和匯出是靜態的,import 會自動提升到程式碼的頂層 ,import , export 不能放在塊級作用域或條件語句中。
  2. ES6 module 和 Common.js 一樣,對於相同的 js 檔案,會儲存靜態屬性。
    但是與 Common.js 不同的是 ,CommonJS 模組同步載入並執行模組檔案,ES6 模組提前載入並執行模組檔案,ES6 模組在預處理階段分析模組依賴,在執行階段執行模組,兩個階段都採用深度優先遍歷,執行順序是子 -> 父。
  3. es module import匯入的屬性是read only的,直接修改會報錯;commonjs require匯入的是屬性的copy,可以更改匯入屬性。

總結

Commonjs 特性總結

  • CommonJS 模組由 JS 執行時實現。
  • CommonJs 是單個值匯出,本質上匯出的就是 exports 屬性。
  • CommonJS 是可以動態載入的,對每一個載入都存在快取,可以有效的解決迴圈引用問題。
  • CommonJS 模組同步載入並執行模組檔案。

ES Module特性總結

  • ES6 Module 靜態的,不能放在塊級作用域內,程式碼發生在編譯時。
  • ES6 Module 的值是動態繫結的,可以通過匯出方法修改,可以直接訪問修改結果。
  • ES6 Module 可以匯出多個屬性和方法,可以單個匯入匯出,混合匯入匯出。
  • ES6 模組提前載入並執行模組檔案,
  • ES6 Module 的特性可以很容易實現 Tree Shaking 和 Code Splitting。
四體不勤 五穀不分 文不能測字 武不能防身