開啟webpeck-cli下的convert-argv.js檔案
// 定義options為空陣列
const options = [];
// webpack -d 檢查 -d指令
if (argv.d) {
//...
}
// webpack -p
if (argv.p) {
//...
}
if (argv.output) {
//...
}
//...
/*如果有 --config --config webpack.config.js config就是webpack.config.js
可以這樣理解
"dev": "webpack-dev-server --inline --progress --hot --config webpack.config.js",當我們npm run dev的時候執行這段
package.json的內容 此時有config讀取webpack.config.js的內容 當我們npm run build時 執行 "webpack" 此時沒有config走else分支*/
if (argv.config) {
// ... 獲取檔案
}else{
/*讀取預設配置 ts co 等字尾類
defaultConfigFiles是 陣列[{ path:
`/Users/orion/Desktop/react-beauty-highcharts/webpack.config.js`,
ext: `.js`
},{path:`/Users/orion/Desktop/react-beauty-highcharts/webpack.config.ts`, ext: `.ts`},{},...] */
for (i = 0; i < defaultConfigFiles.length; i++) {
const webpackConfig = defaultConfigFiles[i].path;
// 讀取檔案,如果有的話push推進去
if (fs.existsSync(webpackConfig)) {
configFiles.push({
path: webpackConfig,
ext: defaultConfigFiles[i].ext
});
// 最終結果configFiles is the Array [ { path:`/Users/orion/Desktop/react-beauty-highcharts/webpack.config.js`,
// ext: `.js` } ]
break;
}
}
}
process.cwd() 是node.js裡讀取檔案路徑的一個API
//configFiles長度大於0時
if (configFiles.length > 0) {
// ...
const requireConfig = function requireConfig(configPath) {
// 這是區域性options不要和全域性的options陣列混淆
let options = (function WEBPACK_OPTIONS() {
if (argv.configRegister && argv.configRegister.length) {
module.paths.unshift(
path.resolve(process.cwd(), "node_modules"),
process.cwd()
);
argv.configRegister.forEach(dep => {
require(dep);
});
return require(configPath);
} else {
// 讀取路徑下的檔案內容返回
return require(configPath);
}
})();
// 預處理options,options若是陣列的話,處理成物件之類的
options = prepareOptions(options, argv);
return options;
};
configFiles.forEach(function(file) {
/// interpret.extensions[.js]為null
// 這裡直接跳出
registerCompiler(interpret.extensions[file.ext]);
// options這裡是全域性options空陣列
options.push(requireConfig(file.path));
});
// configFileLoaded 載入完畢
configFileLoaded = true;
}
// 如果沒有載入完畢,呼叫函式傳遞空陣列
if (!configFileLoaded) {
return processConfiguredOptions({});
} else if (options.length === 1) {
// 如果只有一個,把僅有的傳進去
return processConfiguredOptions(options[0]);
} else {
// 傳options
return processConfiguredOptions(options);
}
注意了,這裡有一個return 也就是這個convert-argv模組的最終返回結果,函式到這裡就結束了。接下來我看看一下processConfiguredOptions函式
我們先按照npm run build分支走options.length為1,讀取options[0]是webpack.config.js裡的module.exports ={} 物件,饒了這大的一個圈子,那麼接下來一起來看一看對你的輸入配置做了怎麼樣的處理吧?
function processConfiguredOptions(options) {
// 非法輸出型別檢測
const webpackConfigurationValidationErrors = validateSchema(
webpackConfigurationSchema,
options
);
if (webpackConfigurationValidationErrors.length) {
const error = new WebpackOptionsValidationError(
webpackConfigurationValidationErrors
);
// 報錯處理,具體什麼是非法,不影響主流程先過?
console.error(
error.message,
`
Received: ${typeof options} : ${JSON.stringify(options, null, 2)}`
);
process.exit(-1); // eslint-disable-line
}
// 如果options是Promise?,以後待查,按照我們的npm run build 流程走不是Promise
if (typeof options.then === "function") {
return options.then(processConfiguredOptions);
}
/* process ES6 default 檢測,雖說我們們的webpack.congfig.js 的內容是用的 ES6 module.exports 寫的,但是options是物件已經被讀出來了,所以不走這個分支,
可是為什麼要再寫一遍呢?看來還是有這種情況的,繼續往下讀*/
if (typeof options === "object" && typeof options.default === "object") {
return processConfiguredOptions(options.default);
}
// filter multi-config by name,對於多配置的處理,我們先不走這個分支
if (Array.isArray(options) && argv["config-name"]) {
//...
}
if (Array.isArray(options)) {
options.forEach(processOptions);
} else {
/* 看了這個多判斷終於有沒有找到家的感覺,回家的路程好艱辛,接下來我們看看processOptions函式對options做了什麼
それは何をしましたか?? */
processOptions(options);
}
// ...
// 這個return 正好對應上個return,是最終的模組返回值
return options;
function processOptions(options) {
// 基本處理函式
function ifArg(name, fn, init, finalize) {
//...
}
function ifArgPair(name, fn, init, finalize){
//...
}
function ifBooleanArg(name, fn) {
//...
}
}
我們看到processOptions先定義了幾個函式
ifArg,ifArgPair,ifBooleanArg,loadPlugin 根據名字可以翻譯
如果是引數,如果是鍵值對形式引數 如果是布林引數,如果是外掛
提起外掛,是不是就想起了我們如下寫的 plugins,對這個配置的處理,讀原始碼聯想大法和通過名稱翻譯的方法還是很重要的,自己寫的時候,儘量起好懂的名稱,不要用中文拼音?
{
plugins: [
new HtmlWebpackPlugin({ // 例項化生成html外掛
title: `title`,
template: `./src/index.html`,
filename: `index.html`,
inlineSource: `.(js|css))$`,
minify: {
removeComments: true,
collapseWhitespace: true
},
chunks: ["index"]
}),
new HtmlWebpackPlugin()
],
}
接下來是真正的函式呼叫
ifArg("mode", function(value) {
options.mode = value;
});
function ifArg(name, fn, init, finalize) {
const isArray = Array.isArray(argv[name]);
const isSet = typeof argv[name] !== "undefined" && argv[name] !== null;
// isArray為false 直接返回
if (!isArray && !isSet) return;
init && init();
if (isArray) argv[name].forEach(fn);
else if (isSet) fn(argv[name], -1);
finalize && finalize();
}
- 說一下argv目前argv是這麼個物件
- webpakck -p 有- p的時候有p=true
- 有 -d的時候有d=true
- `$0`是webpack的目前地址
- 其他的自己悟,哈哈哈哈哈? 聽說90後喜歡用這個表情
{
_: [],
cache: null,
bail: null,
profile: null,
color: [Function: getSupportLevel],
colors: [Function: getSupportLevel],
d: true,
p: true,
`info-verbosity`: `info`,
infoVerbosity: `info`,
`$0`:
`/Users/orion/Desktop/react-beauty-highcharts/node_modules/.bin/webpack`,
debug: true,
`output-pathinfo`: true,
devtool: `eval-cheap-module-source-map`,
mode: `development`,
`optimize-minimize`: true,
define: [ `process.env.NODE_ENV="production"` ]
}
我們接著讀這個函式
function ifArg(name, fn, init, finalize) {
//如果argv[name]是陣列
const isArray = Array.isArray(argv[name]);
//如果argv[name]存在
const isSet = typeof argv[name] !== "undefined" && argv[name] !== null;
// isArray和isSet同時為false的時候返回
if (!isArray && !isSet) return;
// 按照目前的流程走,能過來的name有define,output-pathinfo,debug,devtool,optimize-minimize
//如果init函式存在就執行init函式,目前流程只有define有init,執行函式defineObject = {};
init && init();
//如果是陣列,執行傳入的fn
if (isArray) argv[name].forEach(fn);
else if (isSet) fn(argv[name], -1);
finalize && finalize();
}
這裡只有一個是陣列define,我們追蹤define
ifArgPair(
"define",
function(name, value) {
if (name === null) {
name = value;
value = true;
}
defineObject[name] = value;
},
function() {
defineObject = {};
},
function() {
const DefinePlugin = require("webpack").DefinePlugin;
addPlugin(options, new DefinePlugin(defineObject));
}
);
}
function ifArgPair(name, fn, init, finalize) {
ifArg(
name,
function(content, idx) {
const i = content.indexOf("=");
if (i < 0) {
return fn(null, content, idx);
} else {
// 根據
return fn(content.substr(0, i), content.substr(i + 1), idx);
}
},
init,
finalize
);
}
// 接著,ifArgPair會呼叫ifArg對fn進行了處理
從fn到
function(content, idx) {
const i = content.indexOf("=");
if (i < 0) {
return fn(null, content, idx);
} else {
// 根據
return fn(content.substr(0, i), content.substr(i + 1), idx);
}
}
// 目前define是陣列,define =[ `process.env.NODE_ENV="production"` ]
if (isArray) argv[name].forEach(fn);
// 執行fn 的引數把 等號分割,執行
function(name, value) {
// name = process.env.NODE_ENV
// value = production
if (name === null) {
name = value;
value = true;
}
defineObject[name] = value;
}
// 函式執行結果就是
defineObject.process.env.NODE_ENV = production
// 最後返回的是options,此後還有執行第三個引數出入了defineObject最終影響了options配置
我們接著往下讀
//如果不為陣列,而且存在執行fn
else if (isSet) fn(argv[name], -1);
我們追蹤output-pathinfo
ifBooleanArg("output-pathinfo", function() {
ensureObject(options, "output");
options.output.pathinfo = true;
});
function ifBooleanArg(name, fn) {
ifArg(name, function(bool) {
if (bool) {
// 如果配置是true那麼執行函式
fn();
}
});
}
// 函式執行結果就是
options.output.pathinfo = true;
再往下讀
finalize && finalize();
對於defin欄位春入了finalize函式,即第三個函式
function() {
// 從webpack引入DefinePlugin物件
const DefinePlugin = require("webpack").DefinePlugin;
// 呼叫addPlugin
addPlugin(options, new DefinePlugin(defineObject));
}
function addPlugin(options, plugin) {
ensureArray(options, "plugins");
options.plugins.unshift(plugin);
}
// 最終結果對options做了處理
其他同理
?這個模組終於寫完了
我們總結一下
這個模組做的就是從webpack配置檔案讀取配置賦值給options,然後根據配置,對options做了附加的物件處理比如options.output.pathinfo = true;然後返回options,國外小哥哥好能繞
我們在bin/cli檔案裡列印一下最後的config
{ entry: `./src/index.js`,
output:
{ filename: `index.js`,
path: `/Users/orion/Desktop/react-beauty-highcharts/dist`,
libraryTarget: `commonjs2`,
pathinfo: true },
module: { rules: [ [Object], [Object] ] },
devServer: { openPage: `./src/index.html`, open: true, hot: true },
externals: [ [Function] ],
mode: `development`,
plugins:
[ LoaderOptionsPlugin { options: [Object] },
LoaderOptionsPlugin { options: [Object] },
DefinePlugin { definitions: [Object] } ],
devtool: `eval-cheap-module-source-map`,
context: `/Users/orion/Desktop/react-beauty-highcharts` }
鼓掌?給自己一朵小紅花?