一文深入理解webpack实现原理,不再对编译报错无所适从

713 阅读19分钟

我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情

nodejs、webpack都是openjs的项目,它的支持者中也是一大票美国公司,中国的自主研发的产品在哪儿,仅仅在在这一块,核心技术在哪儿?令人不快,只能学习借鉴。

接着之前的内容,在编译后的内容中,webpackBootstrap是在哪儿生成的呢?

image-20220819090015516.png

这涉及到了chunk的业务。

根据module和chunk.time获得翻译的结果,如下:

image-20220819095546544.png

再往前倒,看这个chunk是怎么来的。

往前倒,查看它的引用会发现,它是renderMain下的 inlineModules,如下:

image-20220819095755494.png

那这个inlineModules是从哪儿来的?关键是要搞清楚,这个m是如何从chunk中转换的?再往前倒:

image-20220819101224231.png

通过chunk获取对应ChunkGraphChunk的keys。这里有一个关键的点是如下内容:

_getChunkGraphChunk(chunk) {
  let cgc = this._chunks.get(chunk);
  if (cgc === undefined) {
    cgc = new ChunkGraphChunk();
    this._chunks.set(chunk, cgc);
  }
  return cgc;
}

这里的 this._chunks 是 new WeakMap() ,它实际上应该是作为了缓存,当再次使用 chunk 作为键的时候从 this._chunks 中通过 get 取值。

到这一步可以看到它对应的inlineModules下的条目对应的 _source 的值还没有被转换,它还是main.js的原生内容:

image-20220819151423461.png

接着找这个源码编译的具体位置,到这一步还没有被编译,还是原生的内容:

image-20220819153907815.png

从这个hooks.renderStartup这个名字好像能看出来点意思,渲染启动的钩子方法。

但是这里只是添加到source,并没有调用钩子函数。而是执行完后回到了Compilation,如下:

image-20220819154429442.png

alreadyWrittenFiles从名字上来看是已经写好的文件,但是它的用处是什么,得看后面的情况:

image-20220819165419051.png

读取资源:

image-20220819173031250.png

这个能看到提供解析的文件路径:

image-20220819173258530.png

这个地方会出现style-loader

image-20220819174037545.png

image-20220819174132294.png

会反复进入到这个buildModule模块:

image-20220819174637333.png

再往回倒,看这个 this._chunks 在赋值过程中发生的事情。在这一步骤会聚焦在 Compliation下的seal方法中:

image-20220819133001922.png

其中getModule如下: image-20220819133341666.png

查找引用模块的依赖项:

image-20220819133559720.png

connection下的module的_source如下:

image-20220819134036327.png

从这个到最后的输出到bundle.js中的内容还是有区别的

image-20220819175744309.png

这一点比较重要。这其实也给看源码提供了一个思路,就是说当我们想要知道某一变量的状态的时候,可以从两个维度去考虑,一个是流程,一个是它本身。如果流程上无法判断它的衍变过程的情况下,就直接定位它本身通过逆推去寻找线索。

image-20220820064502718.png

通过这种方式可以定位最终生成的bundle.js内容的位置,如下:

image-20220820065149111.png

这里为了方便调试,在当前webpack-resource/lib/RawSource.js下的RawSource构造函数中增加了一个console.log(value)的输出,这样在中断中能够更容易得到对应的结果,如上图。

从RawSource向上一步推断,可以看到output.code就是最终在bundle.js中输出的内容:

image-20220820065739782.png

这一步很关键,这个钩子在什么情况下注册的,它给compilation对象的钩子中processAssets添加处理逻辑:

image-20220820071434846.png

在webpack下调用的位置,也就是说这里除了使用TerserPlugin进行处理之外没有别的逻辑:

image-20220820071946539.png

最后确定,在terser-webpack-pluginx/dist/index.js下的:

image-20220820111155534.png

对应于webpack/Compilation/lib/Compilation中的:

image-20220820111253825.png

当被拦截器拦截后执行 this.optimize。

执行到如下这一步的时候,内容就变成压缩处理后的内容,如下:

image-20220820112111930.png

到这一步基本上搞清楚了,它的输入和输出的转换。但是为什么从es6就转换成了es5,在这里webpack起到了什么作用,loader在哪个环节有意义了,还是没有看到。要看的是它的webpack的函数有什么意义?

把上图中的options.input的内容拿出来看一看:

"/******/ (() => { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 329:\n/***/ ((module) => {\n\n\nfunction init(content)\n{\n    window.document.getElementById('app').innerHTML= content;\n}\n\n\n\nmodule.exports=init;\n\n/***/ }),\n\n/***/ 313:\n/***/ ((module, __webpack_exports__, __webpack_require__) => {\n\n"use strict";\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(81);\n/* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(645);\n/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);\n// Imports\n\n\nvar ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default()));\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, "#app{color:red;}", ""]);\n// Exports\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);\n\n\n/***/ }),\n\n/***/ 645:\n/***/ ((module) => {\n\n"use strict";\n\n\n/*\n  MIT License http://www.opensource.org/licenses/mit-license.php\n  Author Tobias Koppers @sokra\n*/\nmodule.exports = function (cssWithMappingToString) {\n  var list = []; // return the list of modules as css string\n\n  list.toString = function toString() {\n    return this.map(function (item) {\n      var content = "";\n      var needLayer = typeof item[5] !== "undefined";\n\n      if (item[4]) {\n        content += "@supports (".concat(item[4], ") {");\n      }\n\n      if (item[2]) {\n        content += "@media ".concat(item[2], " {");\n      }\n\n      if (needLayer) {\n        content += "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {");\n      }\n\n      content += cssWithMappingToString(item);\n\n      if (needLayer) {\n        content += "}";\n      }\n\n      if (item[2]) {\n        content += "}";\n      }\n\n      if (item[4]) {\n        content += "}";\n      }\n\n      return content;\n    }).join("");\n  }; // import a list of modules into the list\n\n\n  list.i = function i(modules, media, dedupe, supports, layer) {\n    if (typeof modules === "string") {\n      modules = [[null, modules, undefined]];\n    }\n\n    var alreadyImportedModules = {};\n\n    if (dedupe) {\n      for (var k = 0; k < this.length; k++) {\n        var id = this[k][0];\n\n        if (id != null) {\n          alreadyImportedModules[id] = true;\n        }\n      }\n    }\n\n    for (var _k = 0; _k < modules.length; _k++) {\n      var item = [].concat(modules[_k]);\n\n      if (dedupe && alreadyImportedModules[item[0]]) {\n        continue;\n      }\n\n      if (typeof layer !== "undefined") {\n        if (typeof item[5] === "undefined") {\n          item[5] = layer;\n        } else {\n          item[1] = "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {").concat(item[1], "}");\n          item[5] = layer;\n        }\n      }\n\n      if (media) {\n        if (!item[2]) {\n          item[2] = media;\n        } else {\n          item[1] = "@media ".concat(item[2], " {").concat(item[1], "}");\n          item[2] = media;\n        }\n      }\n\n      if (supports) {\n        if (!item[4]) {\n          item[4] = "".concat(supports);\n        } else {\n          item[1] = "@supports (".concat(item[4], ") {").concat(item[1], "}");\n          item[4] = supports;\n        }\n      }\n\n      list.push(item);\n    }\n  };\n\n  return list;\n};\n\n/***/ }),\n\n/***/ 81:\n/***/ ((module) => {\n\n"use strict";\n\n\nmodule.exports = function (i) {\n  return i[1];\n};\n\n/***/ }),\n\n/***/ 181:\n/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {\n\n"use strict";\n__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   "default": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(379);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(795);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(569);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(565);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3__);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(216);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4__);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(589);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5__);\n/* harmony import */ var _node_modules_css_loader_dist_cjs_js_index_css__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(313);\n\n      \n      \n      \n      \n      \n      \n      \n      \n      \n\nvar options = {};\n\noptions.styleTagTransform = (_node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5___default());\noptions.setAttributes = (_node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3___default());\n\n      options.insert = _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2___default().bind(null, "head");\n    \noptions.domAPI = (_node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1___default());\noptions.insertStyleElement = (_node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4___default());\n\nvar update = _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default()(_node_modules_css_loader_dist_cjs_js_index_css__WEBPACK_IMPORTED_MODULE_6__/* ["default"] */ .Z, options);\n\n\n\n\n       /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (_node_modules_css_loader_dist_cjs_js_index_css__WEBPACK_IMPORTED_MODULE_6__/* ["default"] */ .Z && _node_modules_css_loader_dist_cjs_js_index_css__WEBPACK_IMPORTED_MODULE_6__/* ["default"].locals */ .Z.locals ? _node_modules_css_loader_dist_cjs_js_index_css__WEBPACK_IMPORTED_MODULE_6__/* ["default"].locals */ .Z.locals : undefined);\n\n\n/***/ }),\n\n/***/ 379:\n/***/ ((module) => {\n\n"use strict";\n\n\nvar stylesInDOM = [];\n\nfunction getIndexByIdentifier(identifier) {\n  var result = -1;\n\n  for (var i = 0; i < stylesInDOM.length; i++) {\n    if (stylesInDOM[i].identifier === identifier) {\n      result = i;\n      break;\n    }\n  }\n\n  return result;\n}\n\nfunction modulesToDom(list, options) {\n  var idCountMap = {};\n  var identifiers = [];\n\n  for (var i = 0; i < list.length; i++) {\n    var item = list[i];\n    var id = options.base ? item[0] + options.base : item[0];\n    var count = idCountMap[id] || 0;\n    var identifier = "".concat(id, " ").concat(count);\n    idCountMap[id] = count + 1;\n    var indexByIdentifier = getIndexByIdentifier(identifier);\n    var obj = {\n      css: item[1],\n      media: item[2],\n      sourceMap: item[3],\n      supports: item[4],\n      layer: item[5]\n    };\n\n    if (indexByIdentifier !== -1) {\n      stylesInDOM[indexByIdentifier].references++;\n      stylesInDOM[indexByIdentifier].updater(obj);\n    } else {\n      var updater = addElementStyle(obj, options);\n      options.byIndex = i;\n      stylesInDOM.splice(i, 0, {\n        identifier: identifier,\n        updater: updater,\n        references: 1\n      });\n    }\n\n    identifiers.push(identifier);\n  }\n\n  return identifiers;\n}\n\nfunction addElementStyle(obj, options) {\n  var api = options.domAPI(options);\n  api.update(obj);\n\n  var updater = function updater(newObj) {\n    if (newObj) {\n      if (newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap && newObj.supports === obj.supports && newObj.layer === obj.layer) {\n        return;\n      }\n\n      api.update(obj = newObj);\n    } else {\n      api.remove();\n    }\n  };\n\n  return updater;\n}\n\nmodule.exports = function (list, options) {\n  options = options || {};\n  list = list || [];\n  var lastIdentifiers = modulesToDom(list, options);\n  return function update(newList) {\n    newList = newList || [];\n\n    for (var i = 0; i < lastIdentifiers.length; i++) {\n      var identifier = lastIdentifiers[i];\n      var index = getIndexByIdentifier(identifier);\n      stylesInDOM[index].references--;\n    }\n\n    var newLastIdentifiers = modulesToDom(newList, options);\n\n    for (var _i = 0; _i < lastIdentifiers.length; _i++) {\n      var _identifier = lastIdentifiers[_i];\n\n      var _index = getIndexByIdentifier(_identifier);\n\n      if (stylesInDOM[_index].references === 0) {\n        stylesInDOM[_index].updater();\n\n        stylesInDOM.splice(_index, 1);\n      }\n    }\n\n    lastIdentifiers = newLastIdentifiers;\n  };\n};\n\n/***/ }),\n\n/***/ 569:\n/***/ ((module) => {\n\n"use strict";\n\n\nvar memo = {};\n/* istanbul ignore next  */\n\nfunction getTarget(target) {\n  if (typeof memo[target] === "undefined") {\n    var styleTarget = document.querySelector(target); // Special case to return head of iframe instead of iframe itself\n\n    if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) {\n      try {\n        // This will throw an exception if access to iframe is blocked\n        // due to cross-origin restrictions\n        styleTarget = styleTarget.contentDocument.head;\n      } catch (e) {\n        // istanbul ignore next\n        styleTarget = null;\n      }\n    }\n\n    memo[target] = styleTarget;\n  }\n\n  return memo[target];\n}\n/* istanbul ignore next  */\n\n\nfunction insertBySelector(insert, style) {\n  var target = getTarget(insert);\n\n  if (!target) {\n    throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");\n  }\n\n  target.appendChild(style);\n}\n\nmodule.exports = insertBySelector;\n\n/***/ }),\n\n/***/ 216:\n/***/ ((module) => {\n\n"use strict";\n\n\n/* istanbul ignore next  */\nfunction insertStyleElement(options) {\n  var element = document.createElement("style");\n  options.setAttributes(element, options.attributes);\n  options.insert(element, options.options);\n  return element;\n}\n\nmodule.exports = insertStyleElement;\n\n/***/ }),\n\n/***/ 565:\n/***/ ((module, __unused_webpack_exports, __webpack_require__) => {\n\n"use strict";\n\n\n/* istanbul ignore next  */\nfunction setAttributesWithoutAttributes(styleElement) {\n  var nonce =  true ? __webpack_require__.nc : 0;\n\n  if (nonce) {\n    styleElement.setAttribute("nonce", nonce);\n  }\n}\n\nmodule.exports = setAttributesWithoutAttributes;\n\n/***/ }),\n\n/***/ 795:\n/***/ ((module) => {\n\n"use strict";\n\n\n/* istanbul ignore next  */\nfunction apply(styleElement, options, obj) {\n  var css = "";\n\n  if (obj.supports) {\n    css += "@supports (".concat(obj.supports, ") {");\n  }\n\n  if (obj.media) {\n    css += "@media ".concat(obj.media, " {");\n  }\n\n  var needLayer = typeof obj.layer !== "undefined";\n\n  if (needLayer) {\n    css += "@layer".concat(obj.layer.length > 0 ? " ".concat(obj.layer) : "", " {");\n  }\n\n  css += obj.css;\n\n  if (needLayer) {\n    css += "}";\n  }\n\n  if (obj.media) {\n    css += "}";\n  }\n\n  if (obj.supports) {\n    css += "}";\n  }\n\n  var sourceMap = obj.sourceMap;\n\n  if (sourceMap && typeof btoa !== "undefined") {\n    css += "\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))), " */");\n  } // For old IE\n\n  /* istanbul ignore if  */\n\n\n  options.styleTagTransform(css, styleElement, options.options);\n}\n\nfunction removeStyleElement(styleElement) {\n  // istanbul ignore if\n  if (styleElement.parentNode === null) {\n    return false;\n  }\n\n  styleElement.parentNode.removeChild(styleElement);\n}\n/* istanbul ignore next  */\n\n\nfunction domAPI(options) {\n  var styleElement = options.insertStyleElement(options);\n  return {\n    update: function update(obj) {\n      apply(styleElement, options, obj);\n    },\n    remove: function remove() {\n      removeStyleElement(styleElement);\n    }\n  };\n}\n\nmodule.exports = domAPI;\n\n/***/ }),\n\n/***/ 589:\n/***/ ((module) => {\n\n"use strict";\n\n\n/* istanbul ignore next  */\nfunction styleTagTransform(css, styleElement) {\n  if (styleElement.styleSheet) {\n    styleElement.styleSheet.cssText = css;\n  } else {\n    while (styleElement.firstChild) {\n      styleElement.removeChild(styleElement.firstChild);\n    }\n\n    styleElement.appendChild(document.createTextNode(css));\n  }\n}\n\nmodule.exports = styleTagTransform;\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tvar cachedModule = __webpack_module_cache__[moduleId];\n/******/ \t\tif (cachedModule !== undefined) {\n/******/ \t\t\treturn cachedModule.exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\tid: moduleId,\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t(() => {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = (module) => {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\t() => (module['default']) :\n/******/ \t\t\t\t() => (module);\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t})();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t(() => {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = (exports, definition) => {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t})();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t(() => {\n/******/ \t\t__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))\n/******/ \t})();\n/******/ \t\n/******/ \t/* webpack/runtime/make namespace object */\n/******/ \t(() => {\n/******/ \t\t// define __esModule on exports\n/******/ \t\t__webpack_require__.r = (exports) => {\n/******/ \t\t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n/******/ \t\t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n/******/ \t\t\t}\n/******/ \t\t\tObject.defineProperty(exports, '__esModule', { value: true });\n/******/ \t\t};\n/******/ \t})();\n/******/ \t\n/******/ \t/* webpack/runtime/nonce */\n/******/ \t(() => {\n/******/ \t\t__webpack_require__.nc = undefined;\n/******/ \t})();\n/******/ \t\n/************************************************************************/\nvar __webpack_exports__ = {};\n// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.\n(() => {\n__webpack_require__(181);\nconst init=__webpack_require__(329)\ninit('问问计算机:webpack是怎么回事?');\n\n})();\n\n/******/ })()\n;"

用正则表达式替换换行和引号的内容后结果如下:

/******/ (() => { // webpackBootstrap
/******/  var __webpack_modules__ = ({
​
/***/ 329:
/***/ ((module) => {
                function init(content) {
                    window.document.getElementById('app').innerHTML = content;
                }
                module.exports = init;
                /***/
}),
​
/***/ 313:
/***/ ((module, __webpack_exports__, __webpack_require__) => {
                "use strict";
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
                    /* harmony export */
});
/* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(81);
/* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(645);
/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
                // Imports
                var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default()));
                // Module
                ___CSS_LOADER_EXPORT___.push([module.id, "#app{color:red;}", ""]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);
                /***/
}),
​
/***/ 645:
/***/ ((module) => {
                "use strict";
                /*
                  MIT License http://www.opensource.org/licenses/mit-license.php
                  Author Tobias Koppers @sokra
                */
                module.exports = function (cssWithMappingToString) {
                    var list = []; // return the list of modules as css string
​
                    list.toString = function toString() {
                        return this.map(function (item) {
                            var content = "";
                            var needLayer = typeof item[5] !== "undefined";
​
                            if (item[4]) {
                                content += "@supports (".concat(item[4], ") {");
                            }
​
                            if (item[2]) {
                                content += "@media ".concat(item[2], " {");
                            }
​
                            if (needLayer) {
                                content += "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {");
                            }
​
                            content += cssWithMappingToString(item);
​
                            if (needLayer) {
                                content += "}";
                            }
​
                            if (item[2]) {
                                content += "}";
                            }
​
                            if (item[4]) {
                                content += "}";
                            }
​
                            return content;
                        }).join("");
                    }; // import a list of modules into the list
​
​
                    list.i = function i(modules, media, dedupe, supports, layer) {
                        if (typeof modules === "string") {
                            modules = [[null, modules, undefined]];
                        }
​
                        var alreadyImportedModules = {};
​
                        if (dedupe) {
                            for (var k = 0; k < this.length; k++) {
                                var id = this[k][0];
​
                                if (id != null) {
                                    alreadyImportedModules[id] = true;
                                }
                            }
                        }
​
                        for (var _k = 0; _k < modules.length; _k++) {
                            var item = [].concat(modules[_k]);
​
                            if (dedupe && alreadyImportedModules[item[0]]) {
                                continue;
                            }
​
                            if (typeof layer !== "undefined") {
                                if (typeof item[5] === "undefined") {
                                    item[5] = layer;
                                } else {
                                    item[1] = "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {").concat(item[1], "}");
                                    item[5] = layer;
                                }
                            }
​
                            if (media) {
                                if (!item[2]) {
                                    item[2] = media;
                                } else {
                                    item[1] = "@media ".concat(item[2], " {").concat(item[1], "}");
                                    item[2] = media;
                                }
                            }
​
                            if (supports) {
                                if (!item[4]) {
                                    item[4] = "".concat(supports);
                                } else {
                                    item[1] = "@supports (".concat(item[4], ") {").concat(item[1], "}");
                                    item[4] = supports;
                                }
                            }
​
                            list.push(item);
                        }
                    };
​
                    return list;
                };
​
                /***/
}),
​
/***/ 81:
/***/ ((module) => {
                "use strict";
                module.exports = function (i) {
                    return i[1];
                };
                /***/
}),
​
/***/ 181:
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
                "use strict";
                __webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "default": () => (__WEBPACK_DEFAULT_EXPORT__)
                    /* harmony export */
});
/* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(379);
/* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(795);
/* harmony import */ var _node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(569);
/* harmony import */ var _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var _node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(565);
/* harmony import */ var _node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3__);
/* harmony import */ var _node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(216);
/* harmony import */ var _node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4__);
/* harmony import */ var _node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(589);
/* harmony import */ var _node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5__);
/* harmony import */ var _node_modules_css_loader_dist_cjs_js_index_css__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(313);
                var options = {};
                options.styleTagTransform = (_node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5___default());
                options.setAttributes = (_node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3___default());
                options.insert = _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2___default().bind(null, "head");
                options.domAPI = (_node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1___default());
                options.insertStyleElement = (_node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4___default());
                var update = _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default()(_node_modules_css_loader_dist_cjs_js_index_css__WEBPACK_IMPORTED_MODULE_6__/* ["default"] */.Z, options);
       /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (_node_modules_css_loader_dist_cjs_js_index_css__WEBPACK_IMPORTED_MODULE_6__/* ["default"] */.Z && _node_modules_css_loader_dist_cjs_js_index_css__WEBPACK_IMPORTED_MODULE_6__/* ["default"].locals */.Z.locals ? _node_modules_css_loader_dist_cjs_js_index_css__WEBPACK_IMPORTED_MODULE_6__/* ["default"].locals */.Z.locals : undefined);
                /***/
}),
​
/***/ 379:
/***/ ((module) => {
                "use strict";
                var stylesInDOM = [];
                function getIndexByIdentifier(identifier) {
                    var result = -1;
                    for (var i = 0; i < stylesInDOM.length; i++) {
                        if (stylesInDOM[i].identifier === identifier) {
                            result = i;
                            break;
                        }
                    }
                    return result;
                }
                function modulesToDom(list, options) {
                    var idCountMap = {};
                    var identifiers = [];
​
                    for (var i = 0; i < list.length; i++) {
                        var item = list[i];
                        var id = options.base ? item[0] + options.base : item[0];
                        var count = idCountMap[id] || 0;
                        var identifier = "".concat(id, " ").concat(count);
                        idCountMap[id] = count + 1;
                        var indexByIdentifier = getIndexByIdentifier(identifier);
                        var obj = {
                            css: item[1],
                            media: item[2],
                            sourceMap: item[3],
                            supports: item[4],
                            layer: item[5]
                        };
​
                        if (indexByIdentifier !== -1) {
                            stylesInDOM[indexByIdentifier].references++;
                            stylesInDOM[indexByIdentifier].updater(obj);
                        } else {
                            var updater = addElementStyle(obj, options);
                            options.byIndex = i;
                            stylesInDOM.splice(i, 0, {
                                identifier: identifier,
                                updater: updater,
                                references: 1
                            });
                        }
​
                        identifiers.push(identifier);
                    }
​
                    return identifiers;
                }
​
                function addElementStyle(obj, options) {
                    var api = options.domAPI(options);
                    api.update(obj);
​
                    var updater = function updater(newObj) {
                        if (newObj) {
                            if (newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap && newObj.supports === obj.supports && newObj.layer === obj.layer) {
                                return;
                            }
​
                            api.update(obj = newObj);
                        } else {
                            api.remove();
                        }
                    };
​
                    return updater;
                }
​
                module.exports = function (list, options) {
                    options = options || {};
                    list = list || [];
                    var lastIdentifiers = modulesToDom(list, options);
                    return function update(newList) {
                        newList = newList || [];
​
                        for (var i = 0; i < lastIdentifiers.length; i++) {
                            var identifier = lastIdentifiers[i];
                            var index = getIndexByIdentifier(identifier);
                            stylesInDOM[index].references--;
                        }
​
                        var newLastIdentifiers = modulesToDom(newList, options);
​
                        for (var _i = 0; _i < lastIdentifiers.length; _i++) {
                            var _identifier = lastIdentifiers[_i];
​
                            var _index = getIndexByIdentifier(_identifier);
​
                            if (stylesInDOM[_index].references === 0) {
                                stylesInDOM[_index].updater();
​
                                stylesInDOM.splice(_index, 1);
                            }
                        }
​
                        lastIdentifiers = newLastIdentifiers;
                    };
                };
​
                /***/
}),
​
/***/ 569:
/***/ ((module) => {
                "use strict";
                var memo = {};
                /* istanbul ignore next  */
                function getTarget(target) {
                    if (typeof memo[target] === "undefined") {
                        var styleTarget = document.querySelector(target); // Special case to return head of iframe instead of iframe itself
​
                        if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) {
                            try {
                                // This will throw an exception if access to iframe is blocked
                                // due to cross-origin restrictions
                                styleTarget = styleTarget.contentDocument.head;
                            } catch (e) {
                                // istanbul ignore next
                                styleTarget = null;
                            }
                        }
                        memo[target] = styleTarget;
                    }
                    return memo[target];
                }
                /* istanbul ignore next  */
                function insertBySelector(insert, style) {
                    var target = getTarget(insert);
                    if (!target) {
                        throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");
                    }
                    target.appendChild(style);
                }
​
                module.exports = insertBySelector;
                /***/
}),
​
/***/ 216:
/***/ ((module) => {
​
                "use strict";
​
​
                /* istanbul ignore next  */
                function insertStyleElement(options) {
                    var element = document.createElement("style");
                    options.setAttributes(element, options.attributes);
                    options.insert(element, options.options);
                    return element;
                }
​
                module.exports = insertStyleElement;
​
                /***/
}),
​
/***/ 565:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
​
                "use strict";
​
​
                /* istanbul ignore next  */
                function setAttributesWithoutAttributes(styleElement) {
                    var nonce = true ? __webpack_require__.nc : 0;
​
                    if (nonce) {
                        styleElement.setAttribute("nonce", nonce);
                    }
                }
​
                module.exports = setAttributesWithoutAttributes;
​
                /***/
}),
​
/***/ 795:
/***/ ((module) => {
​
                "use strict";
​
​
                /* istanbul ignore next  */
                function apply(styleElement, options, obj) {
                    var css = "";
​
                    if (obj.supports) {
                        css += "@supports (".concat(obj.supports, ") {");
                    }
​
                    if (obj.media) {
                        css += "@media ".concat(obj.media, " {");
                    }
​
                    var needLayer = typeof obj.layer !== "undefined";
​
                    if (needLayer) {
                        css += "@layer".concat(obj.layer.length > 0 ? " ".concat(obj.layer) : "", " {");
                    }
​
                    css += obj.css;
​
                    if (needLayer) {
                        css += "}";
                    }
​
                    if (obj.media) {
                        css += "}";
                    }
​
                    if (obj.supports) {
                        css += "}";
                    }
​
                    var sourceMap = obj.sourceMap;
​
                    if (sourceMap && typeof btoa !== "undefined") {
                        css += "\
/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))), " */");
                    } // For old IE
​
                    /* istanbul ignore if  */
​
​
                    options.styleTagTransform(css, styleElement, options.options);
                }
​
                function removeStyleElement(styleElement) {
                    // istanbul ignore if
                    if (styleElement.parentNode === null) {
                        return false;
                    }
​
                    styleElement.parentNode.removeChild(styleElement);
                }
                /* istanbul ignore next  */
​
​
                function domAPI(options) {
                    var styleElement = options.insertStyleElement(options);
                    return {
                        update: function update(obj) {
                            apply(styleElement, options, obj);
                        },
                        remove: function remove() {
                            removeStyleElement(styleElement);
                        }
                    };
                }
​
                module.exports = domAPI;
​
                /***/
}),
​
/***/ 589:
/***/ ((module) => {
​
                "use strict";
​
​
                /* istanbul ignore next  */
                function styleTagTransform(css, styleElement) {
                    if (styleElement.styleSheet) {
                        styleElement.styleSheet.cssText = css;
                    } else {
                        while (styleElement.firstChild) {
                            styleElement.removeChild(styleElement.firstChild);
                        }
​
                        styleElement.appendChild(document.createTextNode(css));
                    }
                }
​
                module.exports = styleTagTransform;
​
                /***/
})
​
        /******/
});
/************************************************************************/
/******/  // The module cache
/******/  var __webpack_module_cache__ = {};
/******/
/******/  // The require function
/******/  function __webpack_require__(moduleId) {
/******/    // Check if module is in cache
/******/    var cachedModule = __webpack_module_cache__[moduleId];
/******/    if (cachedModule !== undefined) {
/******/      return cachedModule.exports;
            /******/
}
/******/    // Create a new module (and put it into the cache)
/******/    var module = __webpack_module_cache__[moduleId] = {
/******/      id: moduleId,
/******/      // no module.loaded needed
/******/      exports: {}
            /******/
};
/******/
/******/    // Execute the module function
/******/    __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/    // Return the exports of the module
/******/    return module.exports;
        /******/
}
/******/
/************************************************************************/
/******/  /* webpack/runtime/compat get default export */
/******/  (() => {
/******/    // getDefaultExport function for compatibility with non-harmony modules
/******/    __webpack_require__.n = (module) => {
/******/      var getter = module && module.__esModule ?
/******/        () => (module['default']) :
/******/        () => (module);
/******/      __webpack_require__.d(getter, { a: getter });
/******/      return getter;
            /******/
};
        /******/
})();
/******/
/******/  /* webpack/runtime/define property getters */
/******/  (() => {
/******/    // define getter functions for harmony exports
/******/    __webpack_require__.d = (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] });
                    /******/
}
                /******/
}
            /******/
};
        /******/
})();
/******/
/******/  /* webpack/runtime/hasOwnProperty shorthand */
/******/  (() => {
/******/    __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
        /******/
})();
/******/
/******/  /* webpack/runtime/make namespace object */
/******/  (() => {
/******/    // define __esModule on exports
/******/    __webpack_require__.r = (exports) => {
/******/      if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/        Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
                /******/
}
/******/      Object.defineProperty(exports, '__esModule', { value: true });
            /******/
};
        /******/
})();
/******/
/******/  /* webpack/runtime/nonce */
/******/  (() => {
/******/    __webpack_require__.nc = undefined;
        /******/
})();
    /******/
    /************************************************************************/
    var __webpack_exports__ = {};
    // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
    (() => {
        __webpack_require__(181);
        const init = __webpack_require__(329)
        init('问问计算机:webpack是怎么回事?');
    })();
    /******/
})()
    ;

折叠起代码块如下:

image-20220822084433223.png

把这个文件重命名为 bundle.js 放在dist文件夹下,浏览 index.html 效果如下:

image-20220822084816712.png

也就是说这个效果和最终输出的编译压缩后的bundle.js的效果是一样的。但是这个对于查看编译结果的内容来说更加直白。

初始化 webpack_modules 对象,各模块对应的属性名称是 数字,如下:329,313,645,81,181,379,569,216,565,795,589

image-20220822091906410.png

这里在第一次加载的时候实际上它只是给对 webpack_require.n进行了赋值而已,并没有调用

image-20220822092020034.png

后面几个方法同理,只是对 webpack_require.d、 webpack_require.o webpack_require.r、 webpack_require.nc进行赋值:

image-20220822092403148.png

在最后才开始真正的业务实现,如下:

image-20220822092600034.png

调用__webpack_require__(181),先经过如下过程,判断缓存对象中是否存在对应于181的模块,如果存在直接获取,如果不存在久再创建一个新的对应moduleId的模块对象。然后使用 webpack_module[moduleId]执行对应的方法(注:这里的 __webpack_module_cache 只是当前环境下的一个对象,并不是操作系统的角度去理解的物理意义上的缓存机制):

/*
 * __webpack_module_cache 模块缓存对象
 * author:askcomputer
 * **/
var __webpack_module_cache__ = {};
 /*
 * __webpack_require 方法
 * author:askcomputer
 * **/
function __webpack_require__(moduleId) {
  // 判断模块是否在cache中
    var cachedModule = __webpack_module_cache__[moduleId];
    if (cachedModule !== undefined) {
        return cachedModule.exports;
  }
  //如果cache对象中没有,就创建一个新的模块,并把它放在cache中
    var module = __webpack_module_cache__[moduleId] = {
        id: moduleId,
        exports: {}
    };
    // 根据moduleId调用__webpack_modules__对象中对应的模块方法
    __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
    return module.exports;
}

下面执行181对应的方法如下,其中三个参数对应的实参:

image-20220822095004806

{
    ...
    181:
    ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
                    "use strict";
                    __webpack_require__.r(__webpack_exports__);
  ...

这一步骤给 webpack_exports 对象添加属性,并赋值如下:

image-20220822102218758.png

image-20220822102252692.png

紧接着调用 d 方法 如下:

image-20220822102408261.png

image-20220822102519820.png

调用 o 方法,如下:

if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
    Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
}

o 方法:

image-20220822102842676.png

__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))

Object.prototype.hasOwnProperty.call() 用来判断一个属性是定义在对象本身而不是继承自原型链。

image-20220822103119945.png

这种情况下,webpack_require.o(definition, key) 的结果为true,而__webpack_require__.o(exports, key) 的结果为false。很明显,definition 当参数传进来的时候给的 default ,而 exports 在前面定义的只有 __esModule属性和 Symbol(Symbol.toStringTag),所以if后的条件成立,执行if的代码块,如下:

image-20220822103628018.png

执行完后给 exports 对象绑定了get属性,如下:

image-20220822103818159.png

接下来回到模块181,并调用模块379如下:

image-20220822103915246.png

/* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(379);

这一步到这里实际上什么都没有做,它只是给 module.exports 赋值了一个方法,但是并没有执行。如下:

image-20220822104924047.png

紧接着:

image-20220822105034672.png

var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default = __webpack_require__.n(_node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__);

调用n方法,参数是上一步中初始化的模块379。如下:

(() => {
    __webpack_require__.n = (module) => {
    var getter = module && module.__esModule ?
        () => (module['default']) :
        () => (module);
    __webpack_require__.d(getter, { a: getter });
    return getter;
  };
})();

给module设置getter属性,如下:

image-20220822105638398.png

并赋值给_node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default。如下几种同理:

image-20220822110621038.png

下一步看一看 css_loader 的模块,如下:

var _node_modules_css_loader_dist_cjs_js_index_css__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(313);

实现过程类似。

直到 webpack_require(181) 这个方法执行完进入下一步之前所有的内容都是对于 css 的处理,其前提是以webpack 的模块调用过程为基础。

下面的关键就是 这个 Loader 是在什么情况下, 运行的,它和 webpack.config.js 中的配置是什么时候关联上的。

在这其中的loader-runner是干什么用的?

image-20220822161144882.png

image-20220822163118439.png

得到的是一个RawSource对象。

这是解析器解析步骤的一个重要环节:

image-20220822164431160.png

image-20220822165820925.png

这一步比较重要,它涉及到acorn,将原有的source对应的脚本转换成AST格式。

image-20220902174402008.png

content的内容全部加载输出parser完成之后,进入到renderMain的阶段,如下:

image-20220822171313022.png

这里涉及到一个RuntimeTemplate.js的内容,暂且不知道它具体的用处是什么。这里看一看针对上述的输出内容,看它的每一行最终的结果是怎么转换输出的。好像离终点越来越近了。

加载,解析,编译,渲染,输出。

现在这一步到了模板渲染最终结果的阶段了。

但是前面的加载器处理的过程,还需要进一步细化。当前只是知道了,它如何进入加载器。怎么找到加载器,加载器做了哪些操作还需要进一步抽象明确。

image-20220822174639817.png

这儿就是输出的最终的渲染结果的部分。从这再往回倒应该就相对更清晰。

详细看这个streamChunk方法的五个参数:

image-20220823082732867.png

在streamChunk方法中得到的结果对应source和code,然后用于resultSource的后续处理。

其中当前上下文中:

  • source:

image-20220823083238637.png 这里的source的类型是ConcatSource的类型,在ConcatSource类下可以看到streamChunk的方法,所以这里第一个if判断结果为真,执行if条件下的代码块。ConcatSource中的Concat直译是“连接”的意思。比如在JavaScript中的数组就有concat方法,用于两个数组的连接组合。这里的ConcatSource是对不同种类型的Source的连接。这里的ConcatSource会根据类型去关联CachedSource和RawSources的streamChunk。

```
  streamChunks(options, onChunk, onSource, onName) {
    if (!this._isOptimized) this._optimize(); //把字符串部分变更为RawSources类型,其中子节点的顺序保持原样,CachedSource部分不做修改,直接push到newChildren中
    if (this._children.length === 1)
      return this._children[0].streamChunks(options, onChunk, onSource, onName);
    let currentLineOffset = 0;
    let currentColumnOffset = 0;
    let sourceMapping = new Map();
    let nameMapping = new Map();
    const finalSource = !!(options && options.finalSource);
    let code = "";
    let needToCloseMapping = false;
    for (const item of this._children) {
      const sourceIndexMapping = [];
      const nameIndexMapping = [];
      let lastMappingLine = 0;
            //这里嵌套了一层streamChunks,可是这里调用的不是文件中的streamChunk,而是引入的 const streamChunks = require("./helpers/streamChunks"); 
            //generatedLine:生成的行
            //generatedColumn:生成的列
            //source:解析的内容,源代码
      const { generatedLine, generatedColumn, source } = streamChunks(  
        item,
        options,
        // eslint-disable-next-line no-loop-func
        (
          chunk,
          generatedLine,
          generatedColumn,
          sourceIndex,
          originalLine,
          originalColumn,
          nameIndex
        ) => {
          const line = generatedLine + currentLineOffset;
          const column =
            generatedLine === 1
              ? generatedColumn + currentColumnOffset
              : generatedColumn;
          if (needToCloseMapping) {
            if (generatedLine !== 1 || generatedColumn !== 0) {
              onChunk(
                undefined,
                currentLineOffset + 1,
                currentColumnOffset,
                -1,
                -1,
                -1,
                -1
              );
            }
            needToCloseMapping = false;
          }
          const resultSourceIndex =
            sourceIndex < 0 || sourceIndex >= sourceIndexMapping.length
              ? -1
              : sourceIndexMapping[sourceIndex];
          const resultNameIndex =
            nameIndex < 0 || nameIndex >= nameIndexMapping.length
              ? -1
              : nameIndexMapping[nameIndex];
          lastMappingLine = resultSourceIndex < 0 ? 0 : generatedLine;
          if (finalSource) {
            if (chunk !== undefined) code += chunk;
            if (resultSourceIndex >= 0) {
              onChunk(
                undefined,
                line,
                column,
                resultSourceIndex,
                originalLine,
                originalColumn,
                resultNameIndex
              );
            }
          } else {
            if (resultSourceIndex < 0) {
              onChunk(chunk, line, column, -1, -1, -1, -1);
            } else {
              onChunk(
                chunk,
                line,
                column,
                resultSourceIndex,
                originalLine,
                originalColumn,
                resultNameIndex
              );
            }
          }
        },
        (i, source, sourceContent) => {
          let globalIndex = sourceMapping.get(source);
          if (globalIndex === undefined) {
            sourceMapping.set(source, (globalIndex = sourceMapping.size));
            onSource(globalIndex, source, sourceContent);
          }
          sourceIndexMapping[i] = globalIndex;
        },
        (i, name) => {
          let globalIndex = nameMapping.get(name);
          if (globalIndex === undefined) {
            nameMapping.set(name, (globalIndex = nameMapping.size));
            onName(globalIndex, name);
          }
          nameIndexMapping[i] = globalIndex;
        }
      );
      if (source !== undefined) code += source;
      if (needToCloseMapping) {
        if (generatedLine !== 1 || generatedColumn !== 0) {
          onChunk(
            undefined,
            currentLineOffset + 1,
            currentColumnOffset,
            -1,
            -1,
            -1,
            -1
          );
          needToCloseMapping = false;
        }
      }
      if (generatedLine > 1) { //如果生成的行数大于一
        currentColumnOffset = generatedColumn; //当前的column偏移等于生成的列的数量【ME:这里还不太明白怎么回事】
      } else {
        currentColumnOffset += generatedColumn;
      }
      needToCloseMapping =
        needToCloseMapping ||
        (finalSource && lastMappingLine === generatedLine);
      currentLineOffset += generatedLine - 1;
    }
    return {
      generatedLine: currentLineOffset + 1,
      generatedColumn: currentColumnOffset,
      source: finalSource ? code : undefined
    };
  }
```
  • options

image-20220823083318083.png

  • onChunk

image-20220823083451271.png

  • onSource

image-20220823083530764.png

  • onName

image-20220823083550706.png

image-20220823113555253.png

需要确认的是这个方法的详细逻辑,它实际上是根据module去获取对应的运行时结果,关键在于codeGenerationResults对应的get方法的理解:

image-20220823172242992.png

从下图可以看到,module对应的内容:

image-20220823173757796.png

在渲染模块renderModule的时候会用到 this.map ,如下:

image-20220823172939610.png

而这个this.map是在什么时候赋值的呢?如下:

image-20220823173017608.png

调用它的地方位于Compilation,如下:

image-20220823173043263.png

那么下面一个问题就是这里的Module[1003:...]是什么时候添加到 CodeGenerationResults 的 this.map 中的,使用如下方式:

image-20220823174135419.png

这个module来自于:

image-20220823174744769.png

module来自于 _runCodeGenerationJobs

image-20220824084406475.png

需要明确这个方法的过程。到这一步就离转换的起点更近了。

image-20220824084545051.png

这里对于 asyncLib.eachLimit的理解比较重要,它是neo-async的内容,其中第一个参数 jobs 是任务数组,第二个参数是 任务并行执行的数量,第三个参数是对应每个任务的回调函数。

所以在这个基础上就能理解,对应于job当中的module,实际上是来自于jobs对应的处理后调用回调函数传回来的参数。那么这种情况下就要明确jobs的内容。

而jobs来自于_runCodeGenerationJobs的形参。如下:

image-20220824085531807.png

它的调用位置在:

image-20220824085906498.png

然后进一步查看这个jobs的创建过程。从这个方法中可以看到 jobs 是由 this.modules 遍历后处理得到的。这里的this.modules 是Set对象。JavaScript中Set对象是唯一值的集合,每个值在Set中只能出现一次,一个Set可以容纳任何数据类型的任何值。

所以接着要看这个 this.modules 是什么情况下 add 的元素。

image-20220824091737318.png

到这已经触及到了工作中经常说的webpack中将一切视为模块进行打包的理念了。下一步要搞清楚的是这里的模块是什么模块,怎么生成的。

image-20220824092748232.png

一方面需要确认addModule与_addModule之间的关系,另一方面需要进一步确认addModule的调用位置和module的生成位置,如下:

image-20220824092938449.png

下面详细分析这个this.factorizeModule的实现细节。

this.factoryModule,它对应于 Compilation.prototype.factorizeModule :

//这里的factorize直译为“分解”的意思
Compilation.prototype.factorizeModule = (
    function (options, callback) {
        this.factorizeQueue.add(options, callback); 
    }
);

把参数送给了this.factorizeQueue的add方法。factorizeQueue是一个AsyncQueue,AsyncQueue是在webpack中定义的一个异步队列的类型。这一个过程就很关键,理解起来也比较晦涩。

在AsyncQueue的构造函数中定义了4个钩子。如下:

image-20220824094703234.png

当上面执行 this.factorizeQueue.add(options, callback); 方法的时候,就在钩子上调用了 beforeAdd。在add方法中执行了 this.hooks.beforeAdd.callAsync,如下:

image-20220824095458980.png

这里需要搞清的是callAsync发生了什么事情。

但是因为没有通过tap订阅消息,所以这里直接执行回调函数。

查看key和newEntry,如下:

image-20220824103730096.png

image-20220824104055828.png

image-20220824153244992.png

image-20220824161216261.png

最终定位到 module 的生成位置如下:

image-20220824162426977.png

如下:

image-20220824162752352.png

image-20220824162837057.png

从这就能看到对应于Module的唯一身份识别码,起始位1000。

还有一个关键的地方要注意的是loader-runner

image-20220824165135819.png

下面大概捋一捋思路:从createData到Module,增加了很多属性。

image-20220824170132497.png

image-20220824170016498.png

这里用的get,实际上它对应的是add方法,

image-20220824171303372.png

在这个方法里看一看到,它把三个参数也就是 module、runtime和result 三个合成了一个,实际上最后输出的内容,是存在result里的。

image-20220824171448294.png

result生成的位置如下:

image-20220824172555600.png

对应结果中的181找到踪迹了,如下:

image-20220824172234467.png

实际上文件的内容在一开始的时候就已经读成字节流了,如下:

image-20220824173228912.png

image-20220824173241031.png

这里的 this._source 在 codeGenerator的时候被获取:

image-20220824173706268.png

image-20220824173719212.png

现在基本流程基本上清晰了,需要进一步定位的是 loader 也就是 style-loader 和 css-loader 这两个 loader 实际应用的点 在哪儿。

image-20220824181127457.png

解析请求数组:

image-20220824181557149.png

image-20220824181530869.png

image-20220824181449171.png

这样这个过程就关联上了。

知道了什么时候进行的关联,下一步要明确,什么时候进行的转换,如何进行的loader的转换。这个实际上依托于load-runner。

image-20220825090906728.png

image-20220825090841646.png

所以接下来要看看 load-runner是个什么鬼东西。针对loader-runner源码,要明确测试的流程和使用方法。

最新代码附件