為什麼有模組化
- 1.方便程式碼維護
- 2.每個功能放到一個模組內
- 3.解決命名問題,全域性變數汙染問題
常見的模組化
- 1.我們寫方法寫屬性都放在物件裡(單例模式)
- 缺陷宣告的物件也有可能命名衝突,不能完全解決上述問題
var obj = {
a:1,
init(){
}
fn(){
}
}
複製程式碼
- 2.自執行函式(IIFE) 實現模組化的功能,要把最終的結果對外暴露
- 好處
- 1.沒有名字
- 2.有自己單獨的執行作用域
- 3.可以對外暴露一些屬性
- 缺陷
- 瀏覽器的檔案載入(http請求),非同步的問題不易解決
- 好處
(function(){
...;
return XXX
})()
複製程式碼
commonjs規範定義了幾個點(同步的)
- 1.如何宣告一個模組,node中一個檔案就是一個模組
- 2.每個模組都需要匯出最終的結果
module.exports
- 3.每個模組使用其他模組的時候需要使用
require
方法
commonjs的實現流程
就是把檔案讀取出來之後加一個 函式 執行 最終返回的是
module.exports
- 1.每個模組都有一個
require
方法-->Module.prototype.require
- 2.呼叫
Module._load()
載入模組 - 3.呼叫
Module._resolveFilename()
解析出檔案的絕對路徑,並且增加擴充名 - 4.通過
Module._cache[filename]
檢查是否存在快取,如果有直接返回快取模組的module.exports
- 5.檢查是否是內建模組(例如
fs
模組),如果不是,通過new Module(filename)
建立一個模組,模組上有兩個重要的屬性id
&&exports = {}
- 6.將建立的模組放到快取中
Module._cache[filenama] = module
- 7.嘗試載入模組
tryModuleLoad(module, filename)
核心- 1).通過
path.extname(filename)
獲取檔案的副檔名extension
- 2).通過副檔名
extension
去Module._extensions
中找相應的方法讀取檔案content
- 3).通過
Module.wrap(content)
方法將讀取到的內容進行包裝,返回包裝後的結果wrapper
- 4).通過
vm.runInThisContext(wrapper)
返回一個可執行函式compiledWrapper
- 5).
compiledWrapper.call(this.exports, this.exports, require, this, filename, dirname)
執行
- 1).通過
let path = require('path');
let fs = require('fs');
let vm = require('vm');
function req(pathname) {
return Module._load(pathname);
}
function tryModuleLoad(module, filename) {
return module.load(filename);
}
function Module(id) {
this.id = id;
this.exports = {};
}
Module.prototype.load = function (filename) {
let extension = path.extname(filename);
Module._extensions[extension](this);
return this.exports;
}
Module._extensions = {
'.js': function (module) {
let content = fs.readFileSync(module.id, 'utf8');
let wrapper = Module.wrap(content);
let compiledWrapper = vm.runInThisContext(wrapper);
let result = compiledWrapper.call(module.exports, module.exports, req, module)
Module._cache[module.id] = result;
},
'.json': function (module) {
let result = JSON.parse(fs.readFileSync(module.id, 'utf8'));
Module._cache[module.id] = result;
module.exports = result;
}
}
Module._cache = {};
Module._load = function (pathname) {
let filename = Module._resolveFilename(pathname);
let cacheModule = Module._cache[filename];
if (cacheModule) return cacheModule.exports;
/* 此處略掉檢查內部模組 */
let module = new Module(filename);
return tryModuleLoad(module, filename);
}
Module._resolveFilename = function (pathname) {
let absName = path.resolve(__dirname, pathname);
try {
fs.accessSync(absName);
} catch (err) {
let extArr = ['.js', '.json'];
let ext = path.extname(absName);
if (!ext || !extArr.includes(ext)) {
extArr.every((extname, index) => {
let spliceName = absName + extname;
try {
fs.accessSync(spliceName);
absName = spliceName;
return false;
} catch (err) {
if (index == 1) {
throw new Error(`${pathname} is not defined`)
}
return true
}
})
} else {
throw new Error(`${pathname} is not defined`)
}
}
return absName
}
Module.wrap = function (content) {
return Module.wrapper[0] + content + Module.wrapper[1];
}
Module.wrapper = ['(function (exports, require, module, __filename, __dirname) {', '});']
let r = req('./user');
console.log(r);
複製程式碼