>[success] # webpack 啟動做了什么
~~~
1.在使用webpack 的時候我們,我們一般會在'npm scripts' 配置好一些執行指令,在我們
執行這些指令的時候webpack做了什么?我們可以看一下不是簡寫的運行指令
'.\node_modules\.bin\webpack'
2.指令輸入后進入'node\_modules\.bin' 目錄 查找是否存在 webpack.sh 或者 webpack.cmd 文件,
如果存在,就執行,不 存在,就拋出錯誤
3.我是windows 系統來一下看一下' webpack.cmd'里面寫了什么
@IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\webpack\bin\webpack.js" %*
) ELSE (
@SETLOCAL
@SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\webpack\bin\webpack.js" %*
)
4.發現他還會接著去執行'node_modules\webpack\bin\webpack.js',因此這里才是文件的實際入口
~~~
>[info] ## webpack.js 文件做了什么
~~~
1.整個文件打開后發現整個代碼可以分為六大塊
~~~
~~~ js
// node_modules/webpack/bin/webpack.js
// 1.正常執行返回
process.exitCode = 0;
// 2.運行某個命令
const runCommand = (command, args) => {...}
// 3.判斷某個包是否安裝
const isInstalled = packageName => {...}
// 4.webpack可用的CLI:webpacl-cli和webpack-command
const CLIs = {...}
// 5.判斷是否兩個CLI是否安裝了
const installedClis = CLIs.filter(cli=>cli.installed);
// 6.根據安裝數量進行處理
if (installedClis.length === 0) {...} else if
(installedClis.length === 1) {...} else {...}
~~~
>[danger] ##### 第一塊process.exitCode 分析
~~~
1.process.exitCode 為 0,代表程序正常運行;非 0,則代表程序報錯。
注:Node API 文檔中明確寫到,process.exit() 方法以退出狀態 code 指示 Node.js
同步地終止進程。 如果省略 code,則使用成功代碼 0 或 process.exitCode 的值
(如果已設置)退出。
~~~
>[danger] ##### runCommand 運行某個命令
~~~
1.通過源碼發現整個'runCommand' 方法有兩個參數,
1.1.'command' 要運行的進程在這個入口文件源碼里指的是用'yarn' 還是'npm',當我們看到
'安裝處置'這里的邏輯時候就會發現里面有一段參數'const packageManager = isYarn ? "yarn" : "npm";'
1.2.'args' 這個參數是數組,當我們看到 '安裝處置'這里的邏輯時候會發現實際他參數是
'const installOptions = [isYarn ? "add" : "install", "-D"];'
2.其實通過這兩個參數我們能發現,這個就是幫我們拼接安裝包指令的方法,具體他會安裝什么包還要往下看
3.現在這段代碼中'require("child_process")' 是整個安裝過程核心,這個方法是做什么的?
這是一個提供了衍生子進程模塊,下面代碼中用到的'spawn'方法第一個 參數'要運行的命令',
第二個參數'字符串參數的列表' ,第三個參數'是一些配置項',具體的以后研究
注:相當于會拼成 一個'npm install -D 報名'
~~~
[nodejs 文檔對child_process說明](http://nodejs.cn/api/child_process.html)
[spawn方法的說明](http://nodejs.cn/api/child_process.html#child_process_child_process_spawn_command_args_options)
~~~
/**
* @param {string} command process to run
* @param {string[]} args commandline arguments
* @returns {Promise<void>} promise
*/
const runCommand = (command, args) => {
const cp = require("child_process");
return new Promise((resolve, reject) => {
const executedCommand = cp.spawn(command, args, {
stdio: "inherit",
shell: true
});
executedCommand.on("error", error => {
reject(error);
});
executedCommand.on("exit", code => {
if (code === 0) {
resolve();
} else {
reject();
}
});
});
};
~~~
>[danger] ##### isInstalled 判斷某個包是否安裝
~~~
1.這里需要知道的api'require.resolve',這個方法主要做了?
1.1.使用內部的 require() 機制查詢模塊的位置,此操作只返回解析后的文件名,不會加載該模塊。
如果找不到模塊,則會拋出 MODULE_NOT_FOUND 錯誤。
這里要注意的是如果你寫的只是一個名稱,他是從'node_modules' 開始找的
2.因此這個方法將會幫我們判斷某個包是否出現在'node_modules',也就是是否安裝嘍
~~~
[node對這個api的解釋](http://nodejs.cn/api/modules.html#modules_require_resolve_request_options)
[別人文章里的解釋](https://www.jb51.net/article/111869.htm)
* 這里我寫了一個小例子做說明

~~~
/**
* @param {string} packageName name of the package
* @returns {boolean} is the package installed?
*/
const isInstalled = packageName => {
try {
require.resolve(packageName);
return true;
} catch (err) {
return false;
}
};
~~~
>[danger] ##### CLIs webpack可用的CLI
~~~
1.這里其實就是調用'isInstalled' 來查到底安沒安裝webpack 腳手架
~~~
~~~
/**
* @typedef {Object} CliOption
* @property {string} name display name
* @property {string} package npm package name
* @property {string} binName name of the executable file
* @property {string} alias shortcut for choice
* @property {boolean} installed currently installed?
* @property {boolean} recommended is recommended
* @property {string} url homepage
* @property {string} description description
*/
/** @type {CliOption[]} */
const CLIs = [
{
name: "webpack-cli",
package: "webpack-cli",
binName: "webpack-cli",
alias: "cli",
installed: isInstalled("webpack-cli"),
recommended: true,
url: "https://github.com/webpack/webpack-cli",
description: "The original webpack full-featured CLI."
},
{
name: "webpack-command",
package: "webpack-command",
binName: "webpack-command",
alias: "command",
installed: isInstalled("webpack-command"),
recommended: false,
url: "https://github.com/webpack-contrib/webpack-command",
description: "A lightweight, opinionated webpack CLI."
}
];
~~~
>[danger] ##### installedClis判斷是否兩個CLI是否安裝了
~~~
1.這個就很簡單一個filter 過濾方法
~~~
~~~
const installedClis = CLIs.filter(cli => cli.installed);
~~~
>[danger] ##### 根據安裝數量進行處理
~~~
1.整個代碼的最后執行,如果你'webpack-command'和'webpack-cli'任意一個都沒安裝,
那么就會讓你選擇安裝一個
2.如果安裝其中一個,就正常運行
3.如果兩個都安裝了,那就提示刪除一個才能運行
4.path.resolve總是返回一個以相對于當前的工作目錄(working directory)的絕對路徑。
關于這個api'https://blog.csdn.net/iconhot/article/details/89257576'
5.fs.existsSync 判斷文件是否存在
~~~
~~~
if (installedClis.length === 0) { // 如果你'webpack-command'和'webpack-cli'任意一個都沒安裝
const path = require("path");
const fs = require("fs");
const readLine = require("readline");
let notify =
"One CLI for webpack must be installed. These are recommended choices, delivered as separate packages:";
for (const item of CLIs) { // 循環帶有'webpack-command'和'webpack-cli' 這配置項的數組
if (item.recommended) {// 可以發現默認讓你安裝的其實是'webpack-cli',他的這個屬性才是true
notify += `\n - ${item.name} (${item.url})\n ${item.description}`;
}
}
console.error(notify);
// 查詢你根目錄是否有yarn.lock 來決定是用yarn 還是 npm
// path.resolve總是返回一個以相對于當前的工作目錄的絕對路徑。
// fs.existsSync 判斷文件是否存在
// process.cwd() 方法會返回 Node.js 進程的當前工作目錄
const isYarn = fs.existsSync(path.resolve(process.cwd(), "yarn.lock"));
const packageManager = isYarn ? "yarn" : "npm";
const installOptions = [isYarn ? "add" : "install", "-D"];
console.error(
`We will use "${packageManager}" to install the CLI via "${packageManager} ${installOptions.join(
" "
)}".`
);
const question = `Do you want to install 'webpack-cli' (yes/no): `;
// 在控制臺輸入輸出
const questionInterface = readLine.createInterface({
input: process.stdin,
output: process.stderr
});
questionInterface.question(question, answer => {
questionInterface.close();
const normalizedAnswer = answer.toLowerCase().startsWith("y");
if (!normalizedAnswer) {
console.error(
"You need to install 'webpack-cli' to use webpack via CLI.\n" +
"You can also install the CLI manually."
);
process.exitCode = 1;
return;
}
const packageName = "webpack-cli";
console.log(
`Installing '${packageName}' (running '${packageManager} ${installOptions.join(
" "
)} ${packageName}')...`
);
// 執行我們寫安裝包方法
runCommand(packageManager, installOptions.concat(packageName))
.then(() => {
require(packageName); //eslint-disable-line
})
.catch(error => {
console.error(error);
process.exitCode = 1;
});
});
} else if (installedClis.length === 1) {
const path = require("path");
const pkgPath = require.resolve(`${installedClis[0].package}/package.json`);
// eslint-disable-next-line node/no-missing-require
const pkg = require(pkgPath);
// eslint-disable-next-line node/no-missing-require
require(path.resolve(
path.dirname(pkgPath),
pkg.bin[installedClis[0].binName]
));
} else {
console.warn(
`You have installed ${installedClis
.map(item => item.name)
.join(
" and "
)} together. To work with the "webpack" command you need only one CLI package, please remove one of them or use them directly via their binary.`
);
// @ts-ignore
process.exitCode = 1;
}
~~~
>[danger] ##### 總結 圖片來自下面的參考文章
~~~
1.webpack 最終找到 webpack-cli (webpack-command) 這個 npm 包,并且
執行 CLI
~~~

>[danger] ##### 參考的文章
[Webpack 進階之源碼分析(一)](https://segmentfault.com/a/1190000021469703)
- 工程化 -- Node
- vscode -- 插件
- vscode -- 代碼片段
- 前端學會調試
- 谷歌瀏覽器調試技巧
- 權限驗證
- 包管理工具 -- npm
- 常見的 npm ci 指令
- npm -- npm install安裝包
- npm -- package.json
- npm -- 查看包版本信息
- npm - package-lock.json
- npm -- node_modules 層級
- npm -- 依賴包規則
- npm -- install 安裝流程
- npx
- npm -- 發布自己的包
- 包管理工具 -- pnpm
- 模擬數據 -- Mock
- 頁面渲染
- 渲染分析
- core.js && babel
- core.js -- 到底是什么
- 編譯器那些術語
- 詞法解析 -- tokenize
- 語法解析 -- ast
- 遍歷節點 -- traverser
- 轉換階段、生成階段略
- babel
- babel -- 初步上手之了解
- babel -- 初步上手之各種配置(preset-env)
- babel -- 初步上手之各種配置@babel/helpers
- babel -- 初步上手之各種配置@babel/runtime
- babel -- 初步上手之各種配置@babel/plugin-transform-runtime
- babel -- 初步上手之各種配置(babel-polyfills )(未來)
- babel -- 初步上手之各種配置 polyfill-service
- babel -- 初步上手之各種配置(@babel/polyfill )(過去式)
- babel -- 總結
- 各種工具
- 前端 -- 工程化
- 了解 -- Yeoman
- 使用 -- Yeoman
- 了解 -- Plop
- node cli -- 開發自己的腳手架工具
- 自動化構建工具
- Gulp
- 模塊化打包工具為什么出現
- 模塊化打包工具(新) -- webpack
- 簡單使用 -- webpack
- 了解配置 -- webpack.config.js
- webpack -- loader 淺解
- loader -- 配置css模塊解析
- loader -- 圖片和字體(4.x)
- loader -- 圖片和字體(5.x)
- loader -- 圖片優化loader
- loader -- 配置解析js/ts
- webpack -- plugins 淺解
- eslit
- plugins -- CleanWebpackPlugin(4.x)
- plugins -- CleanWebpackPlugin(5.x)
- plugin -- HtmlWebpackPlugin
- plugin -- DefinePlugin 注入全局成員
- webapck -- 模塊解析配置
- webpack -- 文件指紋了解
- webpack -- 開發環境運行構建
- webpack -- 項目環境劃分
- 模塊化打包工具 -- webpack
- webpack -- 打包文件是個啥
- webpack -- 基礎配置項用法
- webpack4.x系列學習
- webpack -- 常見loader加載器
- webpack -- 移動端px轉rem處理
- 開發一個自己loader
- webpack -- plugin插件
- webpack -- 文件指紋
- webpack -- 壓縮css和html構建
- webpack -- 清里構建包
- webpack -- 復制靜態文件
- webpack -- 自定義插件
- wepack -- 關于靜態資源內聯
- webpack -- source map 對照包
- webpack -- 環境劃分構建
- webpack -- 項目構建控制臺輸出
- webpack -- 項目分析
- webpack -- 編譯提速優護體積
- 提速 -- 編譯階段
- webpack -- 項目優化
- webpack -- DefinePlugin 注入全局成員
- webpack -- 代碼分割
- webpack -- 頁面資源提取
- webpack -- import按需引入
- webpack -- 搖樹
- webpack -- 多頁面打包
- webpack -- eslint
- webpack -- srr打包后續看
- webpack -- 構建一個自己的配置后續看
- webpack -- 打包組件和基礎庫
- webpack -- 源碼
- webpack -- 啟動都做了什么
- webpack -- cli做了什么
- webpack - 5
- 模塊化打包工具 -- Rollup
- 工程化搭建代碼規范
- 規范化標準--Eslint
- eslint -- 擴展配置
- eslint -- 指令
- eslint -- vscode
- eslint -- 原理
- Prettier -- 格式化代碼工具
- EditorConfig -- 編輯器編碼風格
- 檢查提交代碼是否符合檢查配置
- 整體流程總結
- 微前端
- single-spa
- 簡單上手 -- single-spa
- 快速理解systemjs
- single-sap 不使用systemjs
- monorepo -- 工程
- Vue -- 響應式了解
- Vue2.x -- 源碼分析
- 發布訂閱和觀察者模式
- 簡單 -- 了解響應式模型(一)
- 簡單 -- 了解響應式模型(二)
- 簡單 --了解虛擬DOM(一)
- 簡單 --了解虛擬DOM(二)
- 簡單 --了解diff算法
- 簡單 --了解nextick
- Snabbdom -- 理解虛擬dom和diff算法
- Snabbdom -- h函數
- Snabbdom - Vnode 函數
- Snabbdom -- init 函數
- Snabbdom -- patch 函數
- 手寫 -- 虛擬dom渲染
- Vue -- minVue
- vue3.x -- 源碼分析
- 分析 -- reactivity
- 好文
- grpc -- 瀏覽器使用gRPC
- grcp-web -- 案例
- 待續