07_webpack_模組化原理和source-map
在webpack的頂層配置中有一個配置叫mode(模式),他其中的值有3種'production、development、none
預設的值為production(生產環境),你會發現webpack解析後的程式碼是經過壓縮和醜化的
因為在你設定mode的時候,他預設代表配置了哪些東西
development |
會將 DefinePlugin 中 process.env.NODE_ENV 的值設定為 development . 為模組和 chunk 啟用有效的名。 |
production |
會將 DefinePlugin 中 process.env.NODE_ENV 的值設定為 production FlagDependencyUsagePlugin ,FlagIncludedChunksPlugin ,ModuleConcatenationPlugin ,NoEmitOnErrorsPlugin 和 TerserPlugin 。 |
none |
不使用任何預設優化選項 |
為了分析為什麼import 和 require經過webpack解析過的檔案能夠支援模組化,所以我們要把mode的值設定為:development
設定為development之後,經過webpack解析後的程式碼不會醜化和壓縮,方便分析
模組化原理
在js檔案中使用CommonJs語法瀏覽器是不支援的,es Module有一些低版本的瀏覽器也不一定支援,但是在開發階段,你在你的js檔案中可以使用你想用的任意的兩種模組化方式,然後再使用webpack進行打包,打包後的檔案你會發現他們都支援模組化了
Webpack打包的程式碼,允許我們使用各種各樣的模組化,但是最常用的是Commonjs和es module
那麼他是如何幫助我們實現了程式碼中支援模組化呢?
我們來研究它的原理
commonJs模組化實現原理
esModule實現原理
commonJs載入esModule的原理
esModule載入CommonJs的原理
commonJs模組化實現原理
建立一個入口檔案common.js
const { dateFormat, priceFormate } = require('./js/format') console.log(dateFormat('abc')); console.log(priceFormate('abc'));
format.js
const dateFormat = (date) => { return "2020-12-12" } const priceFormate = (price) => { return "100.100" } module.exports = { dateFormat, priceFormate }
從webpack打包後的程式碼裡,可以看見很多的eval函式
eval("const { dateFormat, priceFormat, priceFormate } = __webpack_require__(/*! ./js/format */ \"./src/js/format.js\")\r\nconsole.log(dateFormat('abc'));\r\nconsole.log(priceFormate('abc'));\n\n//# sourceURL=webpack://02_webpack/./src/common.js?");
eval("const dateFormat = (date) => {\r\n return \"2020-12-12\"\r\n}\r\n\r\nconst priceFormate = (price) => {\r\n return \"100.100\"\r\n}\r\n\r\nmodule.exports = {\r\n dateFormat,\r\n priceFormate\r\n}\n\n//# sourceURL=webpack://02_webpack/./src/js/format.js?");
現在希望不轉換成eval函式,而是轉換為正常程式碼
在webpack.config.js中設定devtool
在我們的mode為development的時候,devtool預設值為veal
所以我們需要重新設定devtool為source-map
mode: 'development', entry: './src/common.js', devtool: 'source-map',
然後再重新打包
打包完成之後你會發現build下面多了個source-map檔案,我們不需要這個檔案,刪除掉就好了
你再重新開啟bundle.js的時候就不是eval函數了
格式化(刪除註釋、立即執行函式)後的程式碼
//定義了一個物件 // 模組的路徑作為key:函式(value) var __webpack_modules__ = { "./src/js/format.js": (function (module) { const dateFormat = (date) => { return "2020-12-12" } const priceFormate = (price) => { return "100.100" } module.exports = { dateFormat, priceFormate } }) } //定義一個物件,作為載入模組的快取 var __webpack_module_cache__ = {}; //函式,當我們載入一個模組的時候,都會通過這個函式來載入 function __webpack_require__(moduleId) { //檢查是否有快取 var cachedModule = __webpack_module_cache__[moduleId]; if (cachedModule !== undefined) { return cachedModule.exports; } //在快取中根據地址id建立一個物件 var module = __webpack_module_cache__[moduleId] = { exports: {} }; //執行__webpack_modules__傳入路徑id,得到function,傳入建立的module __webpack_modules__[moduleId](module, module.exports, __webpack_require__); return module.exports; } var __webpack_exports__ = {}; //立即執行函式,如果不加前面的感嘆號,這就是一個錯誤的語法 // 加!代表這是一個表示式 //具體開始執行程式碼邏輯 !function () { const { dateFormat, priceFormate } = __webpack_require__("./src/js/format.js") console.log(dateFormat('abc')); console.log(priceFormate('abc')); }();
esModule實現原理
"use strict"; // 1.定義了一個物件,裡面放的是模組對映 var __webpack_modules__ = ({ "./src/js/math.js": (function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { __webpack_require__.r(__webpack_exports__); __webpack_require__.d(__webpack_exports__, { "mul": function () { return mul; }, "sum": function () { return sum; } }); const sum = (num1, num2) => { return num1 + num2; } const mul = (num1, num2) => { return num1 * num2 } }) }); //2.快取 var __webpack_module_cache__ = {}; //3.require函式的實現(載入模組) function __webpack_require__(moduleId) { //檢視是否有快取 var cachedModule = __webpack_module_cache__[moduleId]; if (cachedModule !== undefined) { return cachedModule.exports; } var module = __webpack_module_cache__[moduleId] = { exports: {} }; __webpack_modules__[moduleId](module, module.exports, __webpack_require__); return module.exports; } !function () { //給這個函式物件新增一個屬性:d->function __webpack_require__.d = function (exports, definition) { for (var key in definition) { if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); } } }; }(); !function () { //__webpack_require__這個函式添加了一個屬性:o->funcion,物件是否有某個屬性 __webpack_require__.o = function (obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } }(); !function () { //__webpack_require__這個函式添加了一個屬性:r->funcion //給物件中新增esmodule的標識 __webpack_require__.r = function (exports) { //是否支援Symbol if (typeof Symbol !== 'undefined' && Symbol.toStringTag) { //標識這是ESMODUEL Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); } //標識這是ESMODUEL Object.defineProperty(exports, '__esModule', { value: true }); }; }(); var __webpack_exports__ = {}; //執行函式 !function () { __webpack_require__.r(__webpack_exports__); var _js_math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/js/math.js");
//這種語法的呼叫和直接呼叫函式是一樣的 console.log((0, _js_math__WEBPACK_IMPORTED_MODULE_0__.mul)(20, 30));
console.log((0, _js_math__WEBPACK_IMPORTED_MODULE_0__.sum)(10, 20)); }();
commonJs載入esModule的原理
//模組對映 var __webpack_modules__ = ({ "./src/js/math.js": (function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); __webpack_require__.d(__webpack_exports__, { "mul": function () { return mul; }, "sum": function () { return sum; } }); const sum = (num1, num2) => { return num1 + num2; } const mul = (num1, num2) => { return num1 * num2 } }) }); //快取 var __webpack_module_cache__ = {}; //匯入函式 function __webpack_require__(moduleId) { //查詢快取 var cachedModule = __webpack_module_cache__[moduleId]; if (cachedModule !== undefined) { return cachedModule.exports; } var module = __webpack_module_cache__[moduleId] = { exports: {} }; __webpack_modules__[moduleId](module, module.exports, __webpack_require__); return module.exports; } //代理 !function () { __webpack_require__.d = function (exports, definition) { for (var key in definition) { if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); } } }; }(); //檢測某個物件是否有某個屬性 !function () { __webpack_require__.o = function (obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } }(); //給物件中新增esmodule的標識 !function () { __webpack_require__.r = function (exports) { if (typeof Symbol !== 'undefined' && Symbol.toStringTag) { Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); } Object.defineProperty(exports, '__esModule', { value: true }); }; }(); var __webpack_exports__ = {}; !function () { const math = __webpack_require__("./src/js/math.js") console.log(math.mul(10, 20)); console.log(math.sum(20, 30)); }();
esModule載入CommonJs的原理
var __webpack_modules__ = ({ "./src/js/format.js": (function (module) { const dateFormat = (date) => { return "2020-12-12" } const priceFormate = (price) => { return "100.100" } module.exports = { dateFormat, priceFormate } }) }); var __webpack_module_cache__ = {}; function __webpack_require__(moduleId) { var cachedModule = __webpack_module_cache__[moduleId]; if (cachedModule !== undefined) { return cachedModule.exports; } var module = __webpack_module_cache__[moduleId] = { exports: {} }; __webpack_modules__[moduleId](module, module.exports, __webpack_require__); return module.exports; } !function () { __webpack_require__.n = function (module) { var getter = module && module.__esModule ? function () { return module['default']; } : function () { return module; }; __webpack_require__.d(getter, { a: getter }); return getter; }; }(); !function () { __webpack_require__.d = function (exports, definition) { for (var key in definition) { if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); } } }; }(); !function () { __webpack_require__.o = function (obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } }(); !function () { __webpack_require__.r = function (exports) { if (typeof Symbol !== 'undefined' && Symbol.toStringTag) { Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); } Object.defineProperty(exports, '__esModule', { value: true }); }; }(); var __webpack_exports__ = {}; !function () { "use strict"; __webpack_require__.r(__webpack_exports__); var _js_format__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/js/format.js"); var _js_format__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_js_format__WEBPACK_IMPORTED_MODULE_0__); console.log(_js_format__WEBPACK_IMPORTED_MODULE_0___default().priceFormate()); console.log(_js_format__WEBPACK_IMPORTED_MODULE_0___default().dateFormat()); }();