Webpack essence: Understood as an event-based programming paradigm, a series of plug-ins run
Previous articles:
- Easy Tapable
- Get your hands dirty and implement a simple Webpack
- Relearn webpack4 series of topics
The command line
- Run Webpack through NPM Scripts
- Development environment NPM Run dev
- Production environment NPM Run build
- Run directly through Wepback
- webpack entry.js bundle.js
What happens to this process
After running the command, NPM sends the command line tool to the node_modules/. Bin directory to check whether the webpack.sh or webpack. CMD file exists. Throw an error (node_modules/wepback/bin/wepback. Js)
Startup result: Wepback finally finds the wepback -CLI (webpack-Command) package and executes cli
// Returns normal execution
process.exitCode = 0;
// Run a command
const runCommand = (command, args) = > {
const cp = require("child_process");
return new Promise((resolve, reject) = > {
const executedCommand = cp.spawn(command, args, {});
executedCommand.on("error".error= >reject(error);) ;// Code 0 indicates success, resolve, reject
executedCommand.on("exit".code= >{})}); }// Determine whether a package is installed
onst isInstalled = packageName= > {
try {
require.resolve(packageName);
return true;
} catch (err) {
return false; }};// Wepback available CLI: webpck-cli and webpack-command
const installedClis = CLIs.filter(cli= > cli.installed)
// Determine whether the two CLI are installed according to the number of installed cli
if (installedClis.length === 0) {}
else if (installedClis.length === 1) {}
else {}
Copy the code
webpack-cli
- Introduce YARgs and customize the command line
- Analyze command line parameters and convert them to compile configuration items
- Reference WebPack, compile and build from the configuration items
// wepback-cli handles commands that do not need to be compiled
const NON_COMPILATION_ARGS = [
"init".Create a WebPack configuration file
"migrate".// Migrate the WebPack version
"add".// Add attributes to the WebPack configuration file
"remove".// Remove the attributes from the Webpack configuration file
"serve"./ / run webpack - serve
"generate-loader".// Generate webPack Loader code
"generate-plugin".// Generate webpack plugins
"info" // Returns some information about the local environment
];
const NON_COMPILATION_CMD = process.argv.find(arg= > {
if (arg === "serve") {
global.process.argv = global.process.argv.filter(a= >a ! = ="serve");
process.argv = global.process.argv;
}
return NON_COMPILATION_ARGS.find(a= > a === arg);
});
if (NON_COMPILATION_CMD) {
return require("./prompt-command")(NON_COMPILATION_CMD, ... process.argv); }// Dynamically generate help information by providing commands and grouping parameters through yargs
const yargs = require("yargs").usage(`webpack-cli The ${require(".. /package.json").version
}// pass the input command to config-yargs require("./config-yargs")(yargs); Yargs.parse (process.argv.slice(2), (err, argv, Output) => {} let options = require("./convert-argv")(argv); // Give the parameter setting object to Webpack to execute let compiler = webpack(options);Copy the code
-
Webpack-cli uses ARGS analysis, parameter grouping, and commands are divided into 9 classes:
- Config options: Configures related parameters (file name and runtime environment)
- Basic options: Basic parameters (Entry, debug, Watch, devTool)
- Module Options: Module parameters to set extensions for the Loader
- Output options: Output parameters (Output path, Output file name)
- Advanced options: Record Settings, cache Settings, listen frequency, Bail, etc.
- Considerations Options: Resolving parameters (alias and Resolving file suffix Settings)
- Optimizing options: Optimizing parameters
- Stats Options: indicates statistics parameters
- Options: General parameters (help commands, version information)
-
Webpack-cli Execution result
- Webpack-cli converts configuration files to command line parameters and finally generates configuration option parameter options. Finally, webpack objects will be generated according to the configuration parameter instance, and then handed to Webpack to execute the build process (complier).
Tapable plug-in architecture and Hooks design
- compiler extends Tapable -> compilation extends Tapable
- Tapable is a nodejs-like EventEmitter library that controls the publishing and subscribing of Hook functions and controls the WebPack plugin system. Tapable exposes many Hook classes and provides hooks for plugins to mount
- SyncHook: Synchronization hook
- SyncBailHook: SyncBailHook
- SyncWaterfallHook: Synchronized pipelining hook
- SyncLoopHook: Synchronization loop hook
- AsyncParallelHook: Asynchronous concurrent hook
- AsyncParallelBailHook: Asynchronous concurrent fuse hook
- AsyncSeriesHook: Asynchronous serial hook
- AsyncSeriesBailHook: Asynchronous serial fuse hook
- AsyncSeriesWaterfallHokk: asynchronous through water hook
- Tapable Hooks types
- Hook: suffix for all hooks
- Waterfall: indicates a synchronous method, but it will pass the value to the next hanshun
- -Bail: When the function returns any value, the execution of the function will stop
- Loop: Listeners return true to continue the Loop and undefined to end the Loop
- Sync: indicates a synchronization scheme
- AsyncSeries: asynchronous serial hook
- AsyncParallel: Asynchronous concurrent execution hook
- Tapable exposes all the class methods, new a class method to get the hook we need
- Class takes an array parameter, options, which is not mandatory. Class methods take the same number of arguments, depending on how many arguments are passed
- Bind/subscribe:
-
- Asynchronous: tapAsync/tabPromise/tap
-
- Synchronous: tap
- Execution/release:
-
- Asynchronous: callAsync/promise
-
- Synchronous: call
// Create the hook
const hook = new SyncHook(['arg1'.'arg2'.'arg3'])
// Bind events to the WebPack event stream
hook.tap('hook1'.(arg1, arg2, arg3) = > {console.log(arg1, arg2, arg3)})
/ / execution
hook.call(1.2.3);/ / 1, 2, 3
Copy the code
For details on how to use Tapable, please see Tapable easily
Tapable is associated with Webpack
if (Array.isArray(options)) {
compiler = new MultiCompiler(options.map(options= > webpack(options)));
} else if (typeof options === "object") {
options = new WebpackOptionsDefaulter().process(options);
compiler = new Compiler(options.context);
compiler.options = options;
new NodeEnvironmentPlugin().apply(compiler);
if (options.plugins && Array.isArray(options.plugins)) {
for (const plugin of options.plugins) {
plugin.apply(compiler);
}
}
compiler.hooks.environment.call(); / / the hook
compiler.hooks.afterEnvironment.call();
compiler.options = new WebpackOptionsApply().process(options, compiler);
} else {
throw new Error("Invalid argument: options");
}
if (callback) {
/ /...
compiler.run(callback);
}
return compiler;
};
Copy the code
Webpack overall process
The flow chart
Process analysis
- Webpack compilation is executed in the order of hook calls
- Webbpack is essentially a JS Module Bundler used to package multiple code modules. Bundler starts from a construction portal, parses the code, analyzes the dependencies of code modules, and then combines the dependent code modules together. In JavaScriptbundler, some glue codes are provided so that multiple code modules can work together and reference each other
- After analyzing the dependencies, Webpack will make use of JavaScript Function features to provide some code to integrate the various modules together, that is, wrap each module into a JS Function and provide a method to reference the dependent module. Such as __webpack__require__ in the following example, this can not only avoid variable interference, but also effectively control the execution order
// Pack the modules into a file
================================entry======================================
// entry.js
import { bar } from './bar.js'; // Rely on the./bar.js module
// bar.js
const foo = require('./foo.js'); // Depends on the./foo.js moduleRecursively, until no more dependent module, eventually form a module dependence tree = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = moudles = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =// entry.js
modules['./entry.js'] = function() {
const { bar } = __webpack__require__('./bar.js')}// bar.js
modules['./bar.js'] = function() {
const foo = __webpack__require__('./foo.js')};// foo.js
modules['./foo.js'] = function() {
// ...
}
================================output===========================
// The results of executed code modules are saved here
(function(modules){
const installedModules = {}
function __webpack__require__(id) {
// If it is in installedModules, get it directly
// If not, get function from modules and execute,
// Cache the result in installedModules and return the result} ({})"./entry.js": (function(__webpack_require__){
var bar = __webpack_require__(/ * * / code content)}),"./bar.js": (function(){}),
"./foo.js": (function(){})})Copy the code
All Webpack does is convert AST analysis trees into linked lists
-
webpackOptionsApply
- Convert the normal configuration options parameter to an internal WebPack plug-in
- Use the default list, for example
- output.library -> LibraryTemplatePlugin
- externals -> ExternalsPlugin
-
Module construction and Chunk generation phase
compiler hooks
- Process related
- (before-)run
- (before-/after-)compiler
- make
- (after-)emit
- done
- Monitor related
- watch-run
- watch-close
compilation
- Compiler calls the Compilation lifecycle methods
- addEntry -> addModuleChain
- Finish (Module error reported)
- seal
ModuleFactory
- NormalModuleFactory
- ContextModuleFactory
Module
- NormalModule: common module
- ContextModule: ./src/a ./src/b
- ExternalModule: module.exports = jQuery
- DelegatedModule: manifest
- MultiModule: entry: [‘a’, ‘b’]
build
- Use loader-runner to run loaders
- Parsing by Parser (internally acron)
- ParserPlugins add dependencies
Compilation hooks
- The module related
- build-module
- failed-module
- succeed-module
- Resource generation correlation
- module-asset
- chunck-asset
Optimization is related to SEAL
- (after-)seal
- optimize
- optimize-modules(-basic/advanced)
- after-optimize-modules
- after-optimize-chunks
- after-optimize-tree
- optimize-chunk-modules(-basic/advanced)
Chunk generation algorithm
- 1. Webpack generates a chunk for each module in the entry
- 2. Traverse the module dependency list and add the dependent module to chunk
- 3. If a dependent module is dynamically imported, a new chunk is created based on the module and the dependency continues to be traversed
- 4. Repeat the process until you get all chunks
Files are generated
The end of the whole
After a week of reorganizing my thoughts on webpack4 over the years, it’s time to say goodbye to Webpack4 and hope not to see it again…
Webpack 5 beckons, read webPack 5 source code, come to a conclusion: all webpack techniques and add-ons before V5, loader… All abandoned, MY f * * king heart broken, no more jokes…
Stay tuned for webpack5
He’s coming, he’s coming, he’s coming with the green hat
Join us at ❤️
Bytedance Xinfoli team
Nice Leader: Senior technical expert, well-known gold digger columnist, founder of Flutter Chinese community, founder of Flutter Chinese community open source project, well-known developer of Github community, author of dio, FLY, dsBridge and other well-known open source projects
We look forward to your joining us to change our lives with technology!!
Recruitment link: job.toutiao.com/s/JHjRX8B