模塊就是將一個複雜的程序依據必定的規則(規範)封裝成幾個塊(文件), 並進行組合在一塊兒,塊的內部數據與實現是私有的, 只是向外部暴露一些接口(方法)與外部其它模塊通訊。node
CommonJS規範主要用於服務端編程,加載模塊是同步的,這並不適合在瀏覽器環境
例:編程
//module1.js module.exports = { msg: 'module1', foo() { console.log(this.msg) } } // app.js文件 // 引入module1模塊 let module1 = require('./modules/module1') module1.foo() //module1
AMD規範在瀏覽器環境中異步加載模塊,並且能夠並行加載多個模塊。不過,AMD規範開發成本高,代碼的閱讀和書寫比較困難,模塊定義方式的語義不暢。
例json
//定義有依賴的模塊 define(['module1', 'module2'], function(m1, m2){ return 模塊 }) // 引入使用模塊 require(['module1', 'module2'], function(m1, m2){ 使用m1/m2 })
CMD規範與AMD規範很類似,都用於瀏覽器編程,依賴就近,延遲執行,能夠很容易在Node.js中運行。不過,依賴SPM 打包,模塊的加載邏輯偏重設計模式
//定義有依賴的模塊 define(function(require, exports, module){ //引入依賴模塊(同步) var module2 = require('./module2') //引入依賴模塊(異步) require.async('./module3', function (m3) { }) //暴露模塊 exports.xxx = value }) define(function (require) { var m1 = require('./module1') var m4 = require('./module4') m1.show() m4.show() })
ES6 在語言標準的層面上,實現了模塊功能,並且實現得至關簡單,徹底能夠取代 CommonJS 和 AMD 規範,成爲瀏覽器和服務器通用的模塊解決方案**。瀏覽器
/** 定義模塊 math.js **/ var basicNum = 0; var add = function (a, b) { return a + b; }; export { basicNum, add }; /** 引用模塊 **/ import { basicNum, add } from './math'; function test(ele) { ele.textContent = add(99 + basicNum); }
const vm = require('vm') const fs = require('fs') const path = require('path') function Module (id) { this.id = id this.exports = {} } Module.wrap = function (script) { let wrapper = ['(function(exports,require,module,dirname,filename){', '})']; return wrapper[0] + script + wrapper[1] } Module._resolveFilename = function(filename) { // 1.先查看你傳入的文件是不是正常的, 存不存在若是存在 就加載這個文件 // 2.若是文件不存在,嘗試添加.js .json後綴 let filePath = path.resolve(__dirname, filename); let exists = fs.existsSync(filePath); if (exists) return filePath; let keys = Object.keys(Module._extensions) for (let i = 0; i < keys.length; i++) { let concatPath = filePath + keys[i]; // 嘗試添加後綴 if (fs.existsSync(concatPath)) return concatPath } throw new Error('module not found'); } Module._extensions = {} Module._extensions['.js'] = function(module) { let content = fs.readFileSync(module.id, 'utf8'); let wrapperStr = Module.wrap(content); // 要讓內容包裝函數 (模塊化的解決方案就是包裝一層函數) let fn = vm.runInThisContext(wrapperStr); let thisValue = module.exports; // {} // module 裏有一個屬性 等於 exports // module.exports 和 exports // ***************************************** // let exports = module.exports = {} // exports.a = 'hello' // return module.exports // *********************************** console.log(fn.toString()) fn.call(thisValue,module.exports,req,module,path.dirname(module.id),module.id); } Module._extensions['.json'] = function(module) { let content = fs.readFileSync(module.id, 'utf8'); // 手動將json的結果賦予給 module.exports 屬性 module.exports = JSON.parse(content) } // 自定義模塊查找的問題 若是沒有這個文件,我須要嘗試去加載 .js 後綴再去嘗試加載.json / .node Module._resolveFilename = function(filename) { // 1.先查看你傳入的文件是不是正常的, 存不存在若是存在 就加載這個文件 // 2.若是文件不存在,嘗試添加.js .json後綴 let filePath = path.resolve(__dirname, filename); let exists = fs.existsSync(filePath); if (exists) return filePath; let keys = Object.keys(Module._extensions) for (let i = 0; i < keys.length; i++) { let concatPath = filePath + keys[i]; // 嘗試添加後綴 if (fs.existsSync(concatPath)) return concatPath } throw new Error('module not found'); } Module.prototype.load = function() { // 我要加載當前的模塊 當前模塊就是this let extname = path.extname(this.id); Module._extensions[extname](this); // 設計模式 策略模式 } Module._cache = {} function req (id) { const filename = Module._resolveFilename(id); // 轉化成絕對路徑 // 模塊有沒有被緩存.. if(Module._cache[filename]){ // 屢次引用 返回原來的exports對象 return Module._cache[filename].exports; } let module = new Module(filename); // 根據路徑來建立模塊 // 緩存模塊 module.load(); // 核心就是加載這個模塊 讓模塊中的內容執行,賦予給module.exports // 最終返回的是module.exports Module._cache[filename] = module return module.exports; }