目前,几乎所有业务的开发构建都会用到 webpack 。所以下面这篇文章主要给大家介绍了关于webpack构建的详细流程的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起看看吧。

作为模块加载和打包神器,只需配置几个文件,加载各种 loader 就可以享受无痛流程化开发。但对于 webpack 这样一个复杂度较高的插件集合,它的整体流程及思想对我们来说还是很透明的。

本文旨在搞清楚从命令行下敲下 webpack 命令,或者配置 npm script 后执行 package.json 中的命令,到工程目录下出现打包的后的 bundle 文件的过程中,webpack都替我们做了哪些工作。

测试用webpack版本为 webpack@3.4.1

webpack.config.js中定义好相关配置,包括 entry、output、module、plugins等,命令行执行 webpack 命令,webpack 便会根据配置文件中的配置进行打包处理文件,并生成最后打包后的文件。

第一步:执行 webpack 命令时,发生了什么?(bin/webpack.js)

命令行执行 webpack 时,如果全局命令行中未找到webpack命令的话,执行本地的node-modules/bin/webpack.js 文件。

在bin/webpack.js中使用 yargs库 解析了命令行的参数,处理了 webpack 的配置对象 options,调用 processOptions() 函数。

// 处理编译相关,核心函数 function processOptions(options) { // promise风格的处理,暂时还没遇到这种情况的配置 if(typeof options.then === "function") {...} // 处理传入的options为数组的情况 var firstOptions = [].concat(options)[0]; var statsPresetToOptions = require("../lib/Stats.js").presetToOptions; // 设置输出的options var outputOptions = options.stats; if(typeof outputOptions === "boolean" || typeof outputOptions === "string") { outputOptions = statsPresetToOptions(outputOptions); } else if(!outputOptions) { outputOptions = {}; } // 处理各种现实相关的参数 ifArg("display", function(preset) { outputOptions = statsPresetToOptions(preset); }); ... // 引入lib下的webpack.js,入口文件 var webpack = require("../lib/webpack.js"); // 设置最大错误追踪堆栈 Error.stackTraceLimit = 30; var lastHash = null; var compiler; try { // 编译,这里是关键,需要进入lib/webpack.js文件查看 compiler = webpack(options); } catch(e) { // 错误处理 var WebpackOptionsValidationError = require("../lib/WebpackOptionsValidationError"); if(e instanceof WebpackOptionsValidationError) { if(argv.color) console.error("\u001b[1m\u001b[31m" + e.message + "\u001b[39m\u001b[22m"); else console.error(e.message); process.exit(1); // eslint-disable-line no-process-exit } throw e; } // 显示相关参数处理 if(argv.progress) { var ProgressPlugin = require("../lib/ProgressPlugin"); compiler.apply(new ProgressPlugin({ profile: argv.profile })); } // 编译完后的回调函数 function compilerCallback(err, stats) {} // watch模式下的处理 if(firstOptions.watch || options.watch) { var watchOptions = firstOptions.watchOptions || firstOptions.watch || options.watch || {}; if(watchOptions.stdin) { process.stdin.on("end", function() { process.exit(0); // eslint-disable-line }); process.stdin.resume(); } compiler.watch(watchOptions, compilerCallback); console.log("\nWebpack is watching the files…\n"); } else // 调用run()函数,正式进入编译过程 compiler.run(compilerCallback); }第二步: 调用 webpack,返回 compiler 对象的过程(lib/webpack.js)

如下图所示,lib/webpack.js 中的关键函数为 webpack,其中定义了编译相关的一些操作。

"use strict"; const Compiler = require("./Compiler"); const MultiCompiler = require("./MultiCompiler"); const NodeEnvironmentPlugin = require("./node/NodeEnvironmentPlugin"); const WebpackOptionsApply = require("./WebpackOptionsApply"); const WebpackOptionsDefaulter = require("./WebpackOptionsDefaulter"); const validateSchema = require("./validateSchema"); const WebpackOptionsValidationError = require("./WebpackOptionsValidationError"); const webpackOptionsSchema = require("../schemas/webpackOptionsSchema.json"); // 核心方法,调用该方法,返回Compiler的实例对象compiler function webpack(options, callback) {...} exports = module.exports = webpack; // 设置webpack对象的常用属性 webpack.WebpackOptionsDefaulter = WebpackOptionsDefaulter; webpack.WebpackOptionsApply = WebpackOptionsApply; webpack.Compiler = Compiler; webpack.MultiCompiler = MultiCompiler; webpack.NodeEnvironmentPlugin = NodeEnvironmentPlugin; webpack.validate = validateSchema.bind(this, webpackOptionsSchema); webpack.validateSchema = validateSchema; webpack.WebpackOptionsValidationError = WebpackOptionsValidationError; // 对外暴露一些插件 function exportPlugins(obj, mappings) {...} exportPlugins(exports, {...}); exportPlugins(exports.optimize = {}, {...});

接下来看在webpack函数中主要定义了哪些操作

// 核心方法,调用该方法,返回Compiler的实例对象compiler function webpack(options, callback) { // 验证是否符合格式 const webpackOptionsValidationErrors = validateSchema(webpackOptionsSchema, options); if(webpackOptionsValidationErrors.length) { throw new WebpackOptionsValidationError(webpackOptionsValidationErrors); } let compiler; // 传入的options为数组的情况,调用MultiCompiler进行处理,目前还没遇到过这种情况的配置 if(Array.isArray(options)) { compiler = new MultiCompiler(options.map(options => webpack(options))); } else if(typeof options === "object") { // 配置options的默认参数 new WebpackOptionsDefaulter().process(options); // 初始化一个Compiler的实例 compiler = new Compiler(); // 设置context的默认值为进程的当前目录,绝对路径 compiler.context = options.context; // 定义compiler的options属性 compiler.options = options; // Node环境插件,其中设置compiler的inputFileSystem,outputFileSystem,watchFileSystem,并定义了before-run的钩子函数 new NodeEnvironmentPlugin().apply(compiler); // 应用每个插件 if(options.plugins && Array.isArray(options.plugins)) { compiler.apply.apply(compiler, options.plugins); } // 调用environment插件 compiler.applyPlugins("environment"); // 调用after-environment插件 compiler.applyPlugins("after-environment"); // 处理compiler对象,调用一些必备插件 compiler.options = new WebpackOptionsApply().process(options, compiler); } else { throw new Error("Invalid argument: options"); } if(callback) { if(typeof callback !== "function") throw new Error("Invalid argument: callback"); if(options.watch === true || (Array.isArray(options) && options.some(o => o.watch))) { const watchOptions = Array.isArray(options) ? options.map(o => o.watchOptions || {}) : (options.watchOptions || {}); return compiler.watch(watchOptions, callback); } compiler.run(callback); } return compiler; }

webpack函数中主要做了以下两个操作,