preface

I’m interested in the running logic of Webpack’s packaged code, so I read the code to learn and summarize.

Webpack4 packaging result analysis

First, show the project and the development source and WebPack configuration in the project

The project directory structure is as follows:

src/heading.js

export default() = > {const element=document.createElement('h2')

  element.textContent='Hello World'
  element.addEventListener('click'.() = >{
    alert('Hello webpack')})return element
}
Copy the code

src/index.js

import createHeading from './heading.js'
const createLink =require('./link.js')

const heading = createHeading()
document.body.append(heading)

const link=createLink()
document.body.append(link)
Copy the code

src/link.js

module.exports=() = >{
  const element=document.createElement('a')
  element.textContent='click me'
  return element
}
Copy the code

webpack.config.js

const path=require('path')

module.exports={
  mode:'none'.// The 'None' mode is only responsible for packaging consolidated files, no ugliness or compression. This mode is helpful for analyzing packaged code.
  entry:'./src/index.js'.// Import file
   output: {filename:'bundle.js'.// Package the output
     path:path.join(__dirname,'dist') // The absolute path must be passed}}Copy the code

The structure of the packaged code is as follows, which can be divided into module array and Webpack loading logic:

(function(modules) {
	/ /... Webpack load logic: Declares and executes functions for the Webpack load module}) ([/ /... Module array: an array of source code in the module of modules: [index. Js, heading. Js, link. Js]
]);
Copy the code

It is an immediate function. Function execution is performed immediately by passing the development module to the modules parameter. Let’s first look at the code for the module array:

[ 
/* 0 corresponds to SRC /index.js*/
(function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
// Load the module with index 1 from modules, that is, heading.js
/* harmony import */ var _heading_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 

const createLink =__webpack_require__(2) // Load modules with index 1 from modules, that is, link.js

const heading = Object(_heading_js__WEBPACK_IMPORTED_MODULE_0__["default") ()document.body.append(heading)

const link=createLink()
document.body.append(link)
}),
/* 1 corresponds to heading.js*/
(function(module, __webpack_exports__, __webpack_require__) {

"use strict";
// __webpack_require__.r is used to mark the module exported via ES6 export. Modules exported through CommonJS module.exports do not need to be processed
__webpack_require__.r(__webpack_exports__);
// __webpack_exports__ is equivalent to module.exports. Here is the default property value of export default exported module in module.exports
/* harmony default export */ __webpack_exports__["default"] = (() = >{
  const element=document.createElement('h2')

  element.textContent='Hello World'
  element.addEventListener('click'.() = >{
    alert('Hello webpack')})return element
});
}),
/* 2 corresponds to link.js*/
(function(module.exports) {

module.exports=() = >{
  const element=document.createElement('a')
  element.textContent='click me'
  return element
}
})
]
Copy the code

It can be concluded from the above:

  1. All modules developed will be placed in the order they are introducedmodulesIn the array. The code in the entry file goes to the top of the array
  2. Called when the module needs to import another module__webpack_require__Function to pass in the location of the module to be importedmodulesThe position of
  3. If the module is exported using ES6 export, the following processing is done:
    • __webpack_require__.r(__webpack_exports__): marks the module as ES6 module
    • __webpack_exports__["default"] = ...: Put the module code in__webpack_exports__thedefaultAttribute. The equivalent ofmodule.exports.default=...

Let’s look at the code for the WebPack loading logic (I won’t show you any code that doesn’t involve analysis) :

(function(modules) { // webpackBootstrap
	// The module cache is used to store loaded modules. When a module is called again, it immediately returns The exported value to prevent The code in The module from executing again
	var installedModules = {};
    
	// The require function replaces ES6 import and CommonJS require functions
	function __webpack_require__(moduleId) {
		// Check if module is in cache
		if(installedModules[moduleId]) {
			return installedModules[moduleId].exports;
		}
		// Create a new module (and put it into the cache)
		var module = installedModules[moduleId] = {
			i: moduleId, // Indicates the location index of the module in modules
			l: false.// Flag bit indicating that the module is loaded. True indicates that the module is loaded
			exports: {} // Modules export variables via CommonJS module.exports or ES6 export
		};
		// Execute the module function
		modules[moduleId].call(module.exports, module.module.exports, __webpack_require__);
		// Flag the module as loaded
		module.l = true;
		// Return the exports of the module
		return module.exports;
	}
    
	// define getter function for harmony exports
    // Use ES6. The export
	__webpack_require__.d = function(exports, name, getter) {
		if(! __webpack_require__.o(exports, name)) {
			Object.defineProperty(exports, name, { enumerable: true.get: getter }); }};// define __esModule on exports
    // Define the exports variable {Symbol(symbol.tostringTag): "Module",__esModule: true} when handling modules exported via ES6 export.
	__webpack_require__.r = function(exports) {
		if(typeof Symbol! = ='undefined' && Symbol.toStringTag) {
			Object.defineProperty(exports.Symbol.toStringTag, { value: 'Module' });
		}
		Object.defineProperty(exports.'__esModule', { value: true });
	};
    
    // Load entry module and return exports
    // Load and execute the entry file immediately
	return __webpack_require__(__webpack_require__.s = 0);
})
Copy the code

Summary of the above implementation function, the following work is done:

  1. The statementinstalledModulesUsed to store loaded modules.
  2. The statement__webapck_require__Function is used to reference modules. Various attributes are added to the function to aid introduction. This function runs as follows:
    • checkinstalledModulesIs there a module to be loaded in thereturnTo go out
    • If the module has not been loaded. The statementmodule={i:moduleId,l:false,expoers:{}}Deposit and toinstalledModulesIn the
    • Call sets the execution context tomodule.exportsAfter executing the module code
    • Finally, themodule.exportsAs a resultreturnTo go out
  3. Finally,__webapck_require__(0)Load the entry file and return the module as the result of an immediate function execution.

Extension: When converting an ES6 module, Webpack converts it to a mode compatible with CommonJS modules. Therefore, variables exported in ES6 modules can also be loaded using CommonJS require. For example, for the leading ES6 module heading. Js, it can be loaded with const createHeading = require(‘./heading. Js ‘).default. As for why ES6 modules are compatible with CommonJS modules and not the other way around, it’s probably because CommonJS came out earlier.

Compare the packaging results of Webpack5

Again, package with the original project, but change from Webpack4 to Webpack5. The structure of the packaged code is as follows. Similarly, the code is divided into module array and Webpack loading logic. The webpack loading logic is analyzed first:

(() = >{
	var __webpack_modules__ = ([
    	/ /... Module array: an array of source code in the module of modules: [index. Js, heading. Js, link. Js]
    ])
    
    // Here is the Webpack loading logic: declare and execute functions for the Webpack loading module
    // The Module cache is equivalent to webPack4 installedModules
    var __webpack_module_cache__ = {};
    
    // The require function is equivalent to The function of The same name in Webpack4
    function __webpack_require__(moduleId) {
      // Check if module is in cache
      if(__webpack_module_cache__[moduleId]) {
          return __webpack_module_cache__[moduleId].exports;
      }
      // Create a new module (and put it into the cache)
      var module = __webpack_module_cache__[moduleId] = {
          Id needed // Discard the I and L attributes in WebPack4
          // no module.loaded needed
          exports: {}};// Execute the module function
      // In contrast to Webpack4, the execution context is no longer set. Because every function that contains the module code is in strict mode "use strict", in strict mode this is undefined(in fact webpack4 already uses strict mode.... for all functions)
      __webpack_modules__[moduleId](module.module.exports, __webpack_require__);

      // Return the exports of the module
      return module.exports;
  } 
  
  /* webpack/runtime/define property getters */
  (() = > {
      // define getter functions for Harmony exports // 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}); }; }) ();// startup
  // Load entry module
  // Import the import file directly, instead of returning the import file after loading (already felt unnecessary....)
  __webpack_require__(0);
  // This entry module used 'exports' so it can't be inlined}) ();Copy the code

To summarize the differences between webPack loading logic and WebPack 4:

  1. The first is that the entire structure position has changed. Webpack4 is(function(){webpack load logic})(module array)Webpack5 is(()=>{module array; Webpack loading logic})()
  2. remove__webpack_require__.m.__webpack_require__.c.__webpack_require__.sEtc use fewer attributes
  3. __webpack_require__.d The function is changed to assign all attributes of the passed object tomodule.exportsIn the. Instead of copying the properties specified by the passed object
  4. Finally, only the execution entry file is loaded. The result will not bereturnTo go out

Let’s look at the code in the module array:

var __webpack_modules__ = ([
/* 0 index.js*/
((__unused_webpack_module, __webpack_exports__, __webpack_require__) = > {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _heading_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);

const createLink =__webpack_require__(2)

// use parentheses here :(any,... ,fn2), executes one or more expressions and returns the value of the last expression separated by a comma (,)
const heading = (0,_heading_js__WEBPACK_IMPORTED_MODULE_0__.default)()
document.body.append(heading)

const link=createLink()
document.body.append(link)
}),

/* 1 heading.js*/
((__unused_webpack_module, __webpack_exports__, __webpack_require__) = > {

"use strict";
__webpack_require__.r(__webpack_exports__);
// Copy the value of the second argument to __webpack_require__.d with object.defineProperty
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "default": () = > __WEBPACK_DEFAULT_EXPORT__
/* harmony export */ });
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (() = >{
  const element=document.createElement('h2')

  element.textContent='Hello World'
  element.addEventListener('click'.() = >{
    alert('Hello webpack')})return element
});
}),

/* 2 link.js*/
((module) = > {

module.exports=() = >{
  const element=document.createElement('a')
  element.textContent='click me'
  return element
}
})
]);
Copy the code

To summarize the differences between the module array and Webpack4:

  1. When loading other modules in a module, WebPack4 isObject(_heading_js__WEBPACK_IMPORTED_MODULE_0__["default"])()Webpack5 is(0,_heading_js__WEBPACK_IMPORTED_MODULE_0__.default)(). I don’t see the difference here, or even why they’re written that way.
  2. This parameter is used when processing ES6 modules__webpack_require__.dCopy exported values tomodule.exportsIn the. And through theObject.definePropertymakemodule.exportsAttribute values in cannot be modified.

Afterword.

Webpack itself contact and use quite a long time, quite a lot of things to record, here is a start. After the source code and the use of skills will be written down.