js模塊化以及手撕commonjs代碼

js語言模塊化

模塊就是將一個複雜的程序依據必定的規則(規範)封裝成幾個塊(文件), 並進行組合在一塊兒,塊的內部數據與實現是私有的, 只是向外部暴露一些接口(方法)與外部其它模塊通訊。node

模塊化的好處

  • 避免命名衝突(減小命名空間污染)
  • 更好的分離, 按需加載
  • 更高複用性
  • 高可維護性

模塊化規範

commonjs

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規範在瀏覽器環境中異步加載模塊,並且能夠並行加載多個模塊。不過,AMD規範開發成本高,代碼的閱讀和書寫比較困難,模塊定義方式的語義不暢。
json

//定義有依賴的模塊
define(['module1', 'module2'], function(m1, m2){
   return 模塊
})

// 引入使用模塊
require(['module1', 'module2'], function(m1, m2){
   使用m1/m2
})

CMD規範

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模塊

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);
}

實現commonjs

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;
}
相關文章
相關標籤/搜索