首先導入導出分為幾類
- CommonJS導入CommonJS
- EsModule導入CommonJS
- EsModule導入EsModule
根據實現規則,簡單代碼樣例
// index.js
// import log,{age} from "./log.js"
// // let log = require('./log.js')
// console.log('index.js內容')
// console.log("log", log,age)
// log.js
// CommonJS導出
module.exports ="logger"
// export default "jack"
// export const age = 18;
手寫模擬實現代碼
模塊中的代碼經過 webpack 打包之後,就被放在了一個自執行函數中,這個自調用函數接收一個參數,它的值是一個對象,這個對象的鍵就是相對於當前項目來説, 被打包文件的路徑(在這裏被賦值給了moduleId),它的值就是我們被打包模塊中的源代碼,這個函數定義的時候接收了兩個參數 module exports
在單模塊打包中,且這個模塊中沒有其它的導包操作為例
- 將所有的內容都放置於一個自調用函數(IIFE)中,然後將被打包模塊相關信息進行傳參
- 相關信息就是一個對象,格式就是 moduleId:組裝後的函數( 函數體就是打包前的源碼 )
- 自調用函數體內首先定義一個空對象用於存儲緩存
- 自定義一個 webpack_require 函數,它接收一個 ModuleId
- 這個 moduleId 是在自調用函數體的最後一行調用時傳入的
自調用函數邏輯:
- 判斷當前 ModuleID 對應的模塊是否存在於緩存中
- 如果緩存中不存在的情況下,就自定義的一個 module 存放一個對象
- 同時還將這個 對象存放在了 installModules[moduleId] 裏
- 這個對象有三個屬性: i (存放當前被加載模塊的 moduleId), l(用於標記當前模塊是否已加載, true ) , exports={} (一個容器,在將來調用自己的 require 方法時去存放我們被打包模塊中的內容)
- 通過 Modules[moduleId] 找到最初組裝的那個函數以 call 的方式來調用
- 首先修改了this指向,然後傳入了 module 和 module.exports , 最後還有
一個 webpack_require ,為了將來應對被打包模塊中還有其它的模塊導入
(function (modules) {
// 緩存被加載過的模塊
let installedModules = {};
// 定義一個__webpak_require__方法來替換 import require的加載操作
function __webpack_require__(moduleId) {
// 緩存優先,判斷當前緩存中是否存在要被加載的模塊,如果存在就直接返回
if (installedModules[moduleId]) {
return installedModules[moduleId].exports
}
// 如果當前緩存中不存在,就需要自己定義,執行被導入的模塊內容加載
let module = installedModules[moduleId] = {
i: moduleId, // 存放當前被加載模塊的 moduleId
l: false, // 用於標記當前模塊是否已加載, true
exports: {} // 一個容器,在將來調用自己的 require 方法時去存放我們被打包模塊中的內容
}
// 調用當前moduleId對應的函數,完成內容的加載,__webpack_require__c參數存在是為了解決遞歸調用的問題
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__)
// 當上述方法調用結束之後,修改 l 的值,標識當前模塊內容已經加載完成了
module.l = true
// 加載工作完成之後,將拿回來的內容返回至調用位置
return module.exports
}
// 定義m屬性來保存modules
__webpack_require__.m = modules
// 定義c屬性用於保存cache
__webpack_require__.c = installedModules
// 定義o方法就是接收一個對象,和一個屬性名,然後返回一個布爾值,判斷這個對象的身上是否存在這個屬性
__webpack_require__.o = function (object, property) {
return Object.prototype.hasOwnProperty(object, property)
}
// 定義d方法用於在對象身上添加制定屬性,並且給該屬性提供一個getter
__webpack_require__.d = function (exports, name, getter) {
if (!__webpack_require__.o(exports, name)) {
Object.defineProperty(exports, name, {
enumerable: true,
get: getter
});
}
}
// 定義r方法用於標識當前模塊EsModule
__webpack_require__.r = function (exports) {
if (typeof Symbol !== undefined && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, {
value: "module"
})
}
Object.defineProperty(exports, "__esModule", {
value: true
})
}
// 定義n方法用於設置具體的getter,依據不同的規範下的 module來返回一個相應的 getter
__webpack_require__.n = function (module) {
let getter = module && module.__esModule ? function getDefault() {
return module['default']
} : function getModuleExports() {
return module
}
__webpack_require__.d(getter, 'a', getter)
return getter
}
// 定義p屬性,用於保存資源訪問路徑
__webpack_require__.p = ""
// 調用__webpack_require__方法,執行模塊導入和加載操作
return __webpack_require__(__webpack_require__.s = "./src/index.js")
})(
// CommonJS導入CommonJS
// {
// "./src/index.js": (function (module, exports, __webpack_require__) {
// let log = __webpack_require__( /*! ./log.js */ "./src/log.js")
// console.log('index.js內容')
// console.log("log", log)
// }),
// "./src/log.js": (function (module, exports) {
// // CommonJS導出
// module.exports = "logger"
// })
// }
// EsModule導入CommonJS
{
"./src/index.js": (function (module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
var _log_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( /*! ./log.js */ "./src/log.js");
var _log_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/ __webpack_require__.n(_log_js__WEBPACK_IMPORTED_MODULE_0__);
console.log('index.js內容')
console.log("log", _log_js__WEBPACK_IMPORTED_MODULE_0___default.a)
}),
"./src/log.js": (function (module, exports) {
module.exports = "logger"
})
}
// EsModule導入EsModule
// {
// "./src/index.js": (function (module, __webpack_exports__, __webpack_require__) {
// "use strict";
// __webpack_require__.r(__webpack_exports__);
// var _log_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( /*! ./log.js */ "./src/log.js");
// console.log('index.js內容')
// console.log("log", _log_js__WEBPACK_IMPORTED_MODULE_0__["default"], _log_js__WEBPACK_IMPORTED_MODULE_0__["age"])
// }),
// "./src/log.js": (function (module, __webpack_exports__, __webpack_require__) {
// "use strict";
// __webpack_require__.r(__webpack_exports__);
// __webpack_require__.d(__webpack_exports__, "age", function () {
// return age;
// });
// __webpack_exports__["default"] = ("jack");
// const age = 18;
// })
// }
)
通過編譯生成的index.html,引入手寫built.js文件,控制枱可以正常打印
有一個特殊的t方法,實現如下
// weback原生註釋
// create a fake namespace object
// mode & 1: value is a module id, require it
// mode & 2: merge all properties of value into the ns
// mode & 4: return value when already ns object
// mode & 8|1: behave like require
// 定義t方法,用於加載制定value的模塊內容,之後對內容進行處理再返回.
// 接收二個參數,一個是 value 一般用於表示被加載的模塊id ,第二個值 mode 是一個二進制的數值
__webpack_require__.t = function (value, mode) {
// t 方法內部做的第一件事情就是調用自定義的 require 方法加載value(value一般就是模塊id) 對應的模塊導出,重新賦值給 value
// 當獲取到了這個 value 值之後餘下的 8 4 ns 2 都是對當前的內容進行加工處理,然後返回使用
if (mode & 1) {
value = __webpack_require__(value)
}
// 當mode & 8 成立是直接將 value 返回 ( 1和8同時成立,相當於是commonJS )
if (mode & 8) {
return value
}
// 當 mode & 4 成立時直接將 value 返回(esModule)
if (mode & 4 && typeof vlaue === 'object' && value && value.__esModule) {
return value
}
// 如果8和4都沒有成立則需要自定義ns來通過default屬性返回內容
let ns = Object.create(null)
__webpack_require__.r(ns) // ns通過r方法後會多一個Symbol.toStringTag和__esModule
// 如果拿到的 value 是一個可以直接使用的內容,例如是一個字符串,將它掛載到 ns 的 default 屬性上
Object.defineProperty(ns, 'default', {
enumerable: true,
value: value
})
// 例如返回的是一個對象,進行遍歷,掛載相應key的屬性,還有getter
if (mode & 2 && typeof value !== 'string') {
for (var key in value) {
__webpack_require__.d(ns, key, function () {
// 提供一個getter
return value[key]
}.bind(null, key))
}
}
return ns
}
測試代碼
// t方法測試
{
"./src/index.js": (function (module, exports, __webpack_require__) {
let log = __webpack_require__.t( /*! ./log.js */ "./src/log.js", 0b0111) // 通過第二個值不同設置可以測試不同情況
console.log('index.js內容')
console.log("log", log)
}),
"./src/log.js": (function (module, exports) {
// CommonJS導出
module.exports = "logger"
})
}