1. Webpack profile
Webpack is currently the most popular front-end resource modular management and packaging tool. In simple terms, by specifying entry files, many loose modules are packaged into front-end resources that conform to the deployment of the environment according to dependencies and rules.
Webpack can translate any form of resources, such as CommonJs modules, AMD modules, ES6 modules, CSS, images, JSON, Coffeescript, LESS, etc. into modules for parsing through loaders and plugins.
With hundreds of thousands of modules in the project, how does WebPack organize them together and run in the browser in the order we expect?
Let’s start with these questions. First of all, there are dependencies between modules. How is WebPack organized?
2. How does WebPack handle module dependencies
Here I will not describe, directly on the dry goods figure understanding (the author spent nearly a week, while looking at the source code side to sort out, the source version is: Webpack V5.21.1, WebPack V4.5.0. In later iterations, some function names may change, but the main flow idea and logic should remain the same. Simply put, Webpack uses compiler to control the entire Compilation process, Compilation to specifically build, and finally handing over to Compiler to package files and output them to a specified directory.
The Compilation addModuleTree triggers the processing of module dependencies and calls the ModuleFactory ModuleFactory to complete the parsing. Next, let’s focus on the compilation and packaging part of it.
3. Packaging and source code analysis of different module methods
Let’s start by packaging a sample AMD specification file. (All sample code can be found here)
Prepare a simple JS file
// src/index.js const logA = () =>console.log('hello webpack'); export default logA;Copy the code
Change the entry of the Webpack configuration file to SRC /index.js
// webpack.config.js
...
entry: "./src/index.js",
...
Copy the code
Change the packaging mode of webPack configuration file to AMD,
// webpack.prod.config.js output: { path: path.resolve(__dirname, ".. /dist"), filename: 'amd.js', library: "myDemo", libraryTarget: 'amd'}, Package content, the outermost packaging different define (" myDemo ", [], ((() () = > = > {" use strict "; var e = { 352 : (e, r, o) = >{ o.r(r), o.d(r, { default: () = >t }); const t = function() { return console.log("hello webpack") } } }, r = {}; function o(t) { if (r[t]) return r[t].exports; var n = r[t] = { exports: {} }; return e[t](n, n.exports, o), n.exports } return o.d = (e, r) =>{ for (var t in r) o.o(r, t) && ! o.o(e, t) && Object.defineProperty(e, t, { enumerable: ! 0, get: r[t] }) }, o.o = (e, r) => Object.prototype.hasOwnProperty.call(e, r), o.r = e => { "undefined" ! = typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(e, "__esModule", { value: ! 0 }) }, o(352) })()));Copy the code
We can see that by setting up different packaging specifications, webPack ends up with a very fixed file pattern.
Referring to the previous diagram of the working mechanism, the compilation.seal call (that is, after the module has been built) will call create ChunkAssets (the actual core is the call to template) to reassemble these modules into JS code.
createChunkAssets(callback) { //... asyncLib.forEach( this.chunks, (chunk, callback) => { let manifest = this.getRenderManifest({... }) // Get the render list //... asyncLib.forEach( manifest, (fileManifest, callback) => { //... let source = fileManifest.render(); // Start rendering //... this.emitAsset(file, source, assetInfo); // Package the file and export it to the specified directory //... }, callback); }, callback); }Copy the code
Call the Render function of fileManifest, which is the mainTemplate. MainTemplate passed on to the template to process, that is, call the template. RenderChunkModules, and returns a ConcatSource resources. There are fixed templates, and there are called modules.
static renderChunkModules(renderContext, modules, renderModule, prefix = "") { // 1. Var source = new ConcatSource(); // 2. Handle module dependency update allModules source and render module const allModules = modules.map(module => {return {id: chunkGraph.getModuleId(module), source: renderModule(module) || "false" }; }); Source.add ("{\n"); for (let i = 0; i < allModules.length; i++) { const module = allModules[i]; if (i ! == 0) { source.add(",\n"); } source.add(`\n/***/ ${JSON.stringify(module.id)}:\n`); source.add(module.source); } source.add(`\n\n${prefix}}`); return source; }Copy the code
The ModuleFactory equips the Module with a generator to generate replacement code. During the Generate phase, a request for a RuntimeTemplate is triggered to replace the run-time code. Please refer to the specific compilation process for details (the original link is here)
Finally, output to a file in the specified directory through emitAssets.
4. To summarize
Well, actually Webpack does a lot of things, the source code is also very round, it takes some effort to read… After a preliminary understanding of the general workflow, the next stage focuses on the dependencies between loader and Plugin invocation handling modules. To be continued… (I’m going to grab a cup of tea and catch my breath.)
Sample repository of code specifications presented in this article: github.com/JeanZhao/we…