preface

Webpack is a code compilation and packaging tool, with entry, exit, loader and plug-in, etc. Most front-end developers can skillfully use WebPack to manage our code, but we may not have tried to understand the principle of WebPack compilation. Curious (‘ ヘ´)=3, let’s read the compiled source code and try to understand the basic principle of webpack compilation ヾ(◍°∇) Blue ヾ.

Take the following code for example:

index.html

<! --index.html-->
<html>
   <head>
      <title>Hello World</title>
      <script src='./main.js' />
   </head>
   <body>
      <div id='app'></div>
   </body>
</html
Copy the code

./app/main.js

let message=require('./module1.js');
let app = document.getElementById("app");
app.innerHTML += `<span class="title">${message}</span>`;
Copy the code

./app/module1.js

let message = "Hello World"
module.exports = message;
Copy the code

The Webpack configuration is as follows:

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
	mode:'development'./* Compile configuration */
    devtool: 'eval-source-map'./* Import file */
    entry:  __dirname + "/app/main.js".// The only entry file that has been mentioned several times
    output: {
        path: __dirname + "/dist".// The place where the packed files are stored
        filename: "[name].js"// Output the file name after packaging
    },
    /* Develop the service configuration */
    devServer: {
        contentBase: "./dist".// The directory where the page is loaded by the local server
        historyApiFallback: true.inline: true
    },
	plugins: [new HtmlWebpackPlugin({ // Package the output HTML
		      title: 'Hello World app'.minify: { // Compress HTML files
		        removeComments: true.// Remove comments from HTML
		        collapseWhitespace: true.// Remove whitespace and newline characters
		        minifyCSS: true// Compress inline CSS
		      },
		      filename: 'index.html'.template: 'index.html'}),].Loader * / / * configuration
    module: {
        rules: [{//babel-loader converts the specified file to a browser-recognized JS
                test: /(\.jsx|\.js)$/,
                use: [
					{
					    loader: "babel-loader"}, {loader:"force-strict-loader".options: {sourceMap:true}}].exclude: /node_modules/ // Unprocessed position}]}};Copy the code

The code above will package./app/main.js and app/module1.js as./dist/main.js and import it in the index.html file.

Compiled code

Next, read the compiled./dist/main.js code to get a feel for how package compilation works. To make it easier to read and understand, we have simplified the code as follows.

    (
		function (modules) {
	     	// Cache of loaded modules
			let installedModule = {};
			//CommonJS module load implementation of the core method
			function __webpack_require__(moduleId) {
				if (installedModule[moduleId]) {
					// If the module is loaded, return the module in the cache directly
					return installedModule[moduleId];
				}
				let module = installedModule[moduleId] = {
					id: moduleId,
					loaded: false.// Whether the load is complete
					exports: {} // The external interface of this file module
				}
				// calls the module loading function that executes the module code and assigns a value to module.exports
				modules[moduleId].call(module.exports, module.module.exports, __webpack_require__);

				module.loaded = true; // Load complete
				// Returns the exported value of this module
				return module.exports;
			}
			// Load the entry file
			return__webpack_require__(entry); ({})'./app/main.js': function (module, __webpack_exports__, __webpack_require__) {
			// CommonJs import statements for module1 will be compiled to call the __webpack_require__ loading function,
			// Returns the exported value for module1, that is, the expots property of the Module object for moduleId='./app/module1.js'
			var _module1__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
				/ *! ./module1 */
				"./app/module1.js");
			// Read the contents of the file
			let app = document.getElementById("app");
			app.innerHTML += `<span class="title">${_module1__WEBPACK_IMPORTED_MODULE_0__}</span>`;

		},
		'./app/module1.js': function (module, __webpack_exports__, __webpack_require__) {

			let message = "Hello World"
			// Modify the exports value of its module object
			module.exports = message; }});Copy the code

First webPack compiles each file into a self-executing function, forming a separate scope. This function takes a Modules array object, which is a key-value pair corresponding to the JS file. A few key points to note:

  • entry: Import file
  • installedModule: The module cache object
  • __webpack_require__: CommonJs module system simulation implementation

Simplify the code

To make the code and execution flow more intuitive, further simplify the code.

    // Import file
	let entry = './app/main.js';
	let installedModule={};// Cache of loaded modules
	//CommonJS module load implementation
	function __webpack_require__(moduleId){
	    if(installedModule[moduleId]){
			// If the module is loaded, return the module in the cache directly
			return installedModule[moduleId];
		}
		// Construct a new module object
		let module=installedModule[moduleId]={
			id:moduleId,
			loaded:false.// Whether the load is complete
			exports: {}// The external interface of this file module
		}
		// calls the module loading function that executes the module code and assigns a value to module.exports
		modules[moduleId].call(module.exports,module.module.exports,__webpack_require__);

		module.loaded=true;// Load complete
		// Returns the exported value of this module
		return module.exports;
	}
	// Webpack generates an object corresponding to each js= file, which is a key-value pair {filename: execute function},
	// Executes its own function within a function and recursively calls __webpack_require__ to load other modules on which it depends
	let modules={
		'./app/main.js':function ( module, __webpack_exports__ , __webpack_require__){
			// CommonJs import statements for module1 will be compiled to call the __webpack_require__ loading function,
			// Returns the exported value for module1, that is, the expots property of the Module object for moduleId='./app/module1.js'
			var _module1__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
				/ *! ./module1 */ "./app/module1.js");

			let app = document.getElementById("app");
			app.innerHTML += `<span class="title">${_module1__WEBPACK_IMPORTED_MODULE_0__}</span>`;

		},
		'./app/module1.js':function ( module, __webpack_exports__ , __webpack_require__){

			let message="Hello World"
			// Modify the exports value of its module object
			module.exports=message; }};// Load the entry file
	__webpack_require__(entry);
Copy the code

The above code is easy to understand by referring to the comments provided, and is briefly explained here.

  • The js file is compiled to produce an examplemodulesObject, which is a collection of compiled source code for all JS files, expressed in the form of key-value pairs, the key is the file path, the value is a function to execute the source code;
  • Each JS file corresponds to a Module object, one for each objectexportsProperty, this oneexportsProperty is the external interface used to export file contents;
Exports :{} // moduleId, loaded:false,// exports:{} // moduleId, loaded:false,// exports:{}Copy the code
  • installedModuleIs amoduleObject, all of which are stored as key-value pairsmoduleThe cache.
  • When we go through__webpack_require__(entry)When loading the entry file, we first checkinstalledModuleIf yes, return the cached module directly. If no, create an object and save it in the cache. Then executemodulewithmodulesIn themoduleIdThe corresponding function is executedmoduleSource code, and through modificationmodule.exportsProperty exports the values in this module.
  • If the js file has dependencies on other files, that is, require references to other files, translate tovar _module1__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./app/module1.js");Statement, continue to call__webpack_require__Function into the module,__webpack_require__The function returns the currently imported moduleexportsProperties.

What WebPack does for us is compile the source code we wrote into the form described above. Once we understand the compiled code, we can roughly understand that WebPack needs to do the following things at compile time:

  • Read the contents of the JS file
  • encounterrequireLine is converted into__webpack_require__Form of function
  • Encapsulates the contents of the file as an object, the key is the file path, and the value is a function that executes the compiled JS source code

The above 👆 process is only a general compilation process of JS files. In a real project, there will be multiple Loaders for compiling different types of files, such as vue-loader for compiling. Vue files, CSS-loader for compiling CSS-related codes and files, and TS-loader for compiling. Webpack compiles different types of files based on the loader configured in Modules.

The last

See the following article at 👇 to add your own understanding and code. At the same time, I have also organized a brain map related to webpack principle in the following blog post, which is convenient for memorizing and understanding the process for your reference.

Reference article:Interviewer: Not even webpack? The above is their simple understanding of webpack principle, if there is a mistake also hope to correct, if you have a help welcome to praise 👍 support!