commonjs和ES Module
commonjs匯出方法exports和module.exports的區別?
commonjs
實際匯出的就是exports這個物件,可以把需要匯出的變數、方法等繫結在exports
這個物件上匯出,例如:
//a.js
exports.name = '阿明先森'
console.log(require('./a.js') );
列印:
{
"name": "amingxiansen"
}
小結:所以用exports
匯出的都是物件,匯出內容包含在物件裡。
module.exports
和exports
具有相同的引用。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匯入匯出使用 import
和export
。可以匯出函式、變數、物件等。
export default //預設匯出
===> import temp from './a.js'
export {a, b, c}
===> import {a, b, c} from './a.js'
也可以混合匯入匯出。
es module的特性
- ES6 module 的引入和匯出是靜態的,import 會自動提升到程式碼的頂層 ,import , export 不能放在塊級作用域或條件語句中。
- ES6 module 和 Common.js 一樣,對於相同的 js 檔案,會儲存靜態屬性。
但是與 Common.js 不同的是 ,CommonJS 模組同步載入並執行模組檔案,ES6 模組提前載入並執行模組檔案,ES6 模組在預處理階段分析模組依賴,在執行階段執行模組,兩個階段都採用深度優先遍歷,執行順序是子 -> 父。 - 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。