概述nodejs模組系統核心原理

清風0o0發表於2018-06-23
  • 核心工作原理
    • 模組解析過程(Module Resolve)
    • 模組物件(Module Object)
    • 模組包裝(Module Wrapper)
    • 快取(Cache)
    • 迴圈引用(Cycle)

核心工作原理

模組解析過程(Module Resolve)

test.js

require('x')
複製程式碼
  1. x是node核心模組(如http,zlib等)則返回,否則繼續
  2. 根據Module物件的paths屬性一直遞迴找node_modules資料夾下是否存在該模組,直到根目錄,否則丟擲Error('MODULE_NOT_FOUND')
  3. x是路徑(如/path/to/file)
    • 嘗試LOAD_AS_FILE(如.js,.json,.node),沒有則繼續
    • 嘗試LOAD_AS_DIR(如資料夾下package.json),沒有則丟擲Error('MODULE_NOT_FOUND')

LOAD_AS_FILE:

  1. .js
  2. .json
  3. .node檔案(編譯好的二進位制node外掛)

LOAD_AS_DIR:

  1. X/package.json中的main欄位作為模組入口檔案
  2. index.js
  3. index.json
  4. index.node

模組物件

Module {
  id: '.',
  exports: {},
  parent: null,
  filename: '/Users/wl/Sites/myapp/node-learning/src/module/index.js',
  loaded: false,
  children:
   [
	   Module
	   {
		   id: '/Users/wl/Sites/myapp/node-learning/src/module/a.js',
		   exports: [Object],
		   parent: [Circular],
		   filename: '/Users/wl/Sites/myapp/node-learning/src/module/a.js',
		   loaded: true,
		   children: [Array],
		   paths: [Array]
		}
	],
  paths:
   [
		'/Users/wl/Sites/myapp/node-learning/src/module/node_modules',
		 '/Users/wl/Sites/myapp/node-learning/src/node_modules',
		 '/Users/wl/Sites/myapp/node-learning/node_modules',
		 '/Users/wl/Sites/myapp/node_modules',
		 '/Users/wl/Sites/node_modules',
		 '/Users/wl/node_modules',
		 '/Users/node_modules',
		 '/node_modules'
   ]
}
複製程式碼
  • id 模組id。通常為模組檔案絕對路徑,主模組通常為.
  • exports 模組匯出物件。這裡的exports指的是module.exports
  • parent 父模組。即被依賴模組
  • filename 模組檔名。通常與id相同
  • loaded 模組載入狀態。是否已執行完成
  • children 子模組。即依賴模組
  • paths 模組查詢路徑陣列。

模組包裝(Module Wrapper)

主要由以下兩點考慮

  • 使模組內定義的頂層變數限制在方法(也就是wrapper或者說模組內部)級作用域中,防止汙染global環境

注:建議啟用'use strict'模式,防止定義全域性變數

  • 傳入module, require有利於實現node模組化機制
(function(exports, require, module, __filename, __dirname) {
	// Module code
});
複製程式碼

快取(Cache)

在一個node上下文環境中,兩次require同一個檔案,通常情況下返回完全相同的兩個物件引用。除非用高階函式返回工廠函式。

迴圈引用(Cycle)

由於node包相互依賴,則較大可能會形成迴圈引用,node利用其快取機制避免無限迴圈。比如

index.js

const prefix = '主模組:'
const a = require('./a.js')

console.log(prefix, a)  // {a:2}
console.log(prefix, require.main === module)
console.log(module)
複製程式碼

a.js

'use strict'
const prefix = 'A模組:'
module.exports = {a:1}
const b = require('./b.js')
console.log(prefix, b)  // {b:1}
module.exports = {a:2}
console.log(prefix, require.main === module)
複製程式碼

b.js

const prefix = 'b模組:'
module.exports = {b:1}
const a = require('./a.js')
console.log(prefix, a) // {a:1}
console.log(prefix, require.main === module)
複製程式碼

如上。當b.js引用a.js時,為避免無限迴圈,a.js未完成的副本(我認為是require('./b.js')之前的所有程式碼,但是在官方文件中未得到特別確切的表述)匯出的物件被b.js引用

相關文章