每次啟動項目的服務,電腦竟然乖乖的幫我打開了瀏覽器,100行源碼揭秘!
1. 前言
大家好,我是若川。最近組織了源碼共讀活動,感興趣的可以加我微信 ruochuan12 參與,已進行三個月了,大家一起交流學習,共同進步。
想學源碼,極力推薦之前我寫的《學習源碼整體架構(gòu)系列》 包含jQuery、underscore、lodash、vuex、sentry、axios、redux、koa、vue-devtools、vuex4、koa-compose、vue-next-release、vue-this、create-vue、玩具vite等10余篇源碼文章。
本文倉庫 open-analysis,求個star^_^[1]
最近組織了源碼共讀活動,大家一起學習源碼。于是搜尋各種值得我們學習,且代碼行數(shù)不多的源碼。
我們經(jīng)常遇到類似場景:每次啟動項目的服務,電腦竟然乖乖的幫我打開了瀏覽器。當然你也可能沒有碰到過,但可能有這樣的需求。而源碼300行左右,核心源碼不到100行。跟我們工作息息相關,非常值得我們學習。
之前寫過據(jù)說 99% 的人不知道 vue-devtools 還能直接打開對應組件文件?本文原理揭秘,也是跟本文類似原理。
閱讀本文,你將學到:
1.?電腦竟然乖乖的幫我打開了瀏覽器原理和源碼實現(xiàn)
2.?學會使用?Node.js?強大的?child_process?模塊
3.?學會調(diào)試學習源碼
4.?等等
2. 使用
2.1 在 webpack 中使用
devServer.open[2]
告訴 dev-server[3] 在服務器啟動后打開瀏覽器。將其設置為 true 以打開您的默認瀏覽器。
webpack.config.js
module.exports?=?{
??//...
??devServer:?{
????open:?true,
??},
};
Usage via the CLI:
npx?webpack?serve?--open
To disable:
npx?webpack?serve?--no-open
現(xiàn)在大多數(shù)都不是直接用 webpack 配置了。而是使用腳手架。那么接著來看我們熟悉的腳手架中,打開瀏覽器的功能是怎么使用的。
2.2 在 vue-cli 使用
npx?@vue/cli?create?vue3-project
#?我的?open-analysis?項目中?vue3-project?文件夾
#?npm?i?-g?yarn
#?yarn?serve?不會自動打開瀏覽器
yarn?serve
#?--open?參數(shù)后會自動打開瀏覽器
yarn?serve?--open
2.3 在 create-react-app 使用
npx?create-react-app?react-project
#?我的?open-analysis?項目中?react-project?文件夾
#?npm?i?-g?yarn
#?默認自動打開了瀏覽器
yarn?start

終端我用的是 window terminal,推薦我之前的文章:使用 ohmyzsh 打造 windows、ubuntu、mac 系統(tǒng)高效終端命令行工具,用過都說好。
webpack、vue-cli和create-react-app,它們?nèi)叨加袀€特點就是不約而同的使用了open[4]。
引用 open 分別的代碼位置是:
webpack-dev-server[5] vue-cli[6] create-react-app[7]
接著我們來學習open原理和源碼。
3. 原理
在 npm 之王 @sindresorhus[8] 的 open README文檔[9]中,英文描述中寫了為什么使用它的幾條原因。
為什么推薦使用 open
積極維護。
支持應用參數(shù)。
更安全,因為它使用?spawn?而不是?exec。
修復了大多數(shù) node-open 的問題。
包括適用于 Linux 的最新 xdg-open 腳本。
支持 Windows 應用程序的 WSL 路徑。
一句話概括open原理則是:針對不同的系統(tǒng),使用Node.js的子進程 child_process 模塊的spawn方法,調(diào)用系統(tǒng)的命令打開瀏覽器。
對應的系統(tǒng)命令簡單形式則是:
#?mac
open?https://lxchuan12.gitee.io
#?win
start?https://lxchuan12.gitee.io
#?linux
xdg-open?https://lxchuan12.gitee.io
windows start 文檔[10]
open包描述信息:open[11]
在這里可以看到有哪些 npm 包依賴了 open[12]
我們熟悉的很多 npm 包都依賴了open。這里列舉幾個。
webpack-dev-server[13] react-dev-utils[14] @vue/cli-shared-utils[15] patch-package[16] lighthouse[17] release-it[18]
4. 閱讀源碼前的準備工作
#?推薦克隆我的項目,保證與文章同步,同時測試文件齊全
git?clone?https://github.com/lxchuan12/open-analysis.git
#?npm?i?-g?yarn
cd?open?&&?yarn
#?或者克隆官方項目
git?clone?https://github.com/sindresorhus/open.git
#?npm?i?-g?yarn
cd?open?&&?yarn
4.1 寫個例子,便于調(diào)試源碼
由于測試用例相對較為復雜,我們自己動手寫個簡單的例子,便于我們自己調(diào)試。
根據(jù) README,我們在 open-analysis 文件夾下新建一個文件夾 examples ,里面存放一個 index.js。文件內(nèi)容如下:
//?open-analysis/examples/index.js
(async?()?=>?{
????const?open?=?require('../open/index.js');
????await?open('https://lxchuan12.gitee.io');
})();
在 await open('https://lxchuan12.gitee.io'); 打上斷點。在終端命令行中執(zhí)行
node?examples/index.js
會自動喚起調(diào)試模式。如果不支持先閱讀這個官方文檔配置:Node.js debugging in VS Code[19],如果還是不行,可以升級到最新版VSCode試試。
跟著調(diào)試我們可以進入 open 函數(shù)。


4.2 open 打開函數(shù)
//?open/index.js
const?open?=?(target,?options)?=>?{
?if?(typeof?target?!==?'string')?{
??throw?new?TypeError('Expected?a?`target`');
?}
?return?baseOpen({
??...options,
??target
?});
};
跟著斷點,我們來看最終調(diào)用的 baseOpen。這個函數(shù)比較長,重點可以猜到是:const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions);這句,我們可以打上斷點調(diào)試。
4.3 baseOpen 基礎打開函數(shù)
//?open/index.js
const?childProcess?=?require('child_process');
const?localXdgOpenPath?=?path.join(__dirname,?'xdg-open');
const?{platform,?arch}?=?process;
//?調(diào)試時我們可以自行調(diào)整修改平臺,便于調(diào)試各個平臺異同,比如?mac、win、linux
//?const?{arch}?=?process;
//?mac
//?const?platform?=?'darwin';
//?win
//?const?platform?=?'win32';
//?const?platform?=?'其他';
const?baseOpen?=?async?options?=>?{
????options?=?{
??wait:?false,
??background:?false,
??newInstance:?false,
??allowNonzeroExitCode:?false,
??...options
?};
????//?省略部分代碼
????//?命令
????let?command;
????//?命令行參數(shù)
?const?cliArguments?=?[];
????//?子進程選項
?const?childProcessOptions?=?{};
????if?(platform?===?'darwin')?{
????????command?=?'open';
????????//?省略?mac?部分代碼
????}?else?if?(platform?===?'win32'?||?(isWsl?&&?!isDocker()))?{
????????//?省略?window?或者?window?子系統(tǒng)代碼
????????const?encodedArguments?=?['Start'];
????}?else?{
????????const?useSystemXdgOpen?=?process.versions.electron?||
????????????platform?===?'android'?||?isBundled?||?!exeLocalXdgOpen;
????????command?=?useSystemXdgOpen???'xdg-open'?:?localXdgOpenPath;
????????//?省略?linux?代碼
????}
????//?省略部分代碼
????const?subprocess?=?childProcess.spawn(command,?cliArguments,?childProcessOptions);
????
????//?省略部分代碼
????subprocess.unref();
?return?subprocess;
}
由此我們可以看出:
一句話概括open原理則是:針對不同的系統(tǒng),使用Node.js的子進程 child_process 模塊的spawn方法,調(diào)用系統(tǒng)的命令打開瀏覽器。
對應的系統(tǒng)命令簡單形式則是:
#?mac
open?https://lxchuan12.gitee.io
#?win
start?https://lxchuan12.gitee.io
#?linux
xdg-open?https://lxchuan12.gitee.io
5. 總結(jié)
一句話概括open原理則是:針對不同的系統(tǒng),使用Node.js的子進程 child_process 模塊的spawn方法,調(diào)用系統(tǒng)的命令打開瀏覽器。
本文從日常常見的場景每次啟動服務就能自動打開瀏覽器出發(fā),先講述了日常在webpack、vue-cli、create-react-app如何使用該功能,最后從源碼層面解讀了open[20]的原理和源碼實現(xiàn)。工作常用的知識能做到知其然,知其所以然,就比很多人厲害了。
因為文章不宜過長,所以未全面展開講述源碼中所有細節(jié)。非常建議讀者朋友按照文中方法使用VSCode調(diào)試 open 源碼。學會調(diào)試源碼后,源碼并沒有想象中的那么難。
最后可以持續(xù)關注我@若川。歡迎加我微信 ruochuan12 交流,參與 源碼共讀 活動,大家一起學習源碼,共同進步。
參考資料
本文倉庫 open-analysis,求個star^_^: https://github.com/lxchuan12/open-analysis.git
[2]更多參考資料可以點擊閱讀原文查看
往期推薦

解密初、中、高級程序員的進化之路(前端)

程序員一定會有35歲危機嗎?

近 20k Star的項目說不做就不做了,但總結(jié)的內(nèi)容值得借鑒

但凡早知道這28個網(wǎng)站,都不至于學得那么不扎實
如果你覺得這篇內(nèi)容對你挺有啟發(fā),我想邀請你幫我三個小忙:
點個「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點在看,都是耍流氓 -_-)
歡迎加我微信「huab119」拉你進技術(shù)群,長期交流學習...
關注公眾號「前端勸退師」,持續(xù)為你推送精選好文,也可以加我為好友,隨時聊騷。

如果覺得這篇文章還不錯,來個【轉(zhuǎn)發(fā)、收藏、在看】三連吧,讓更多的人也看到~

