直奔主题,在node中的loader.js 调用 cli.js,在cli.js中包括以下引用
const { checkForBrokenNode, checkForUnsupportedNode } = require('./utils/unsupported.js')
const exitHandler = require('./utils/exit-handler.js')
const Npm = require('./npm.js')
const log = require('./utils/log-shim.js')
const replaceInfo = require('./utils/replace-info.js')
const updateNotifier = require('./utils/update-notifier.js')
小TIPS:对于这种require的引用,因为不同的文件依赖很多,有时候一步步查看会逐渐偏离主线。可以在 internal/modules/cjs/helpers.js
下的
require=function(path){
mod.require(path);
}
的部分添加断点调试,使用单步跳出就可以直接定位到当前调用它的位置。
在cli.js中执行到 await npm.exec(cmd,npm.argv)
这一步比较关键,可以得到 cmd
的值是 run
,而 npm.argv
的值是 dev
。当执行完之后,会发现再次跳入了loader.js
下的 Module.runMain
但是这一次对应的参数变成了 dev 之后的 webpack-dev-server
对应的参数。也就是说从npm、npm-cli的处理流程结束,进入了webpack-dev-server的分析过程。
这个过程从vscode调试窗口中的调用堆栈上也能看到蛛丝马迹,如下:
接下来我们抛开require中通过node调用c++实现函数调用的过程,来直接步入webpack.dev.server.js
的逻辑中。首先来看一下webpack.dev.server.js
通过使用require
引用的依赖图,如下:
以这个骨架图为依据,可以对webpack.dev.server.js
的依赖关系能够一目了然。梳理除外部业务和内部业务,通过这种方式确定范围,有助于各个击破。
这里与要特别提到的一个是上图中的yargs
。要明白的是yargs在这里承担的任务是什么?它的主要目的是解析在命令行中输入的参数,能够让我们更专注的处理业务,而不是去拆分获取命令行中输入的内容。如下图,在yargs.js
中的parseArgs
方法中对参数的解析和赋值过程:
这里弹窗的四个参数对应的就是,package.json
下的 webpack-dev-server --inline --progress --config build/webpack.dev.conf.js
。中webpack-dev-server
后的四个参数。
小TIPS:如果对于深耕webpack中的Loader加载器、rules、plugins的前奏中的require的实现过程不清晰的情况下,就会导致很容易陷入某个单个模块的具体逻辑中无法抽身。最终没有办法从全局掌握webpack的主线。
明确上述的yargs
的使用目的之后,回到 webpack-dev-server.js
中的如下位置:
从上图可以明确,在webpack-dev-server.js
中通过require
调用模块 webpack/bin/convert-arg
,在这里会读取配置文件的路径,如下:
到这一步require
完成之后,--config ./build/webpack.conf.dev.js
这个命令行中配置项的内容才被读取进入内存,称为当前的 wpOpt
常量,如下:
到这一步才是我们熟悉的工程下配置文件中的 context、devServer、devtool、entry、module、node、output、plugins、resolve。而在这之前笔者用了6个篇幅的文章来打基础。
总的来说,之前的内容说的都是从npm运行开始,到加载工程中配置的webpack.dev.conf.js
的过程。
如果参数读取之后端口不为空的情况下,将会启动开发服务器,如下的webpack-dev-server.js
中的内容:
function processOptions(webpackOptions) {
...
if (options.port != null) {
startDevServer(webpackOptions, options);
return;
}
...
}
function startDevServer(webpackOptions, options) {
addDevServerEntrypoints(webpackOptions, options);
}
在前面的依赖图中可以看到,addDevServerEntrypoints
在 ../lib/util/addDevServerEntrypoints
,如下:
...
module.exports = function addDevServerEntrypoints(webpackOptions, devServerOptions, listeningApp) {
if (devServerOptions.inline !== false) {
...
const prependDevClient = (entry) => {
if (typeof entry === 'function') {
...
}
if (typeof entry === 'object' && !Array.isArray(entry)) {
...
}
...
};
[].concat(webpackOptions).forEach((wpOpt) => {
wpOpt.entry = prependDevClient(wpOpt.entry);
});
}
};
从这一段代码中就能够看出来,在配置文件中,对于 entry 入口的配置,可以确定,它支持的方式有:function、object,这两种。(注:可能这就是读源码带来的好处吧,它不仅仅像入门书那样直接告诉你entry的用法和配置,而不知道原有。那么这该死的代码就能让我们清晰可见)