日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

尤雨溪推荐神器 ni ,能替代 npm/yarn/pnpm ?简单好用!源码揭秘!

發布時間:2023/12/9 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 尤雨溪推荐神器 ni ,能替代 npm/yarn/pnpm ?简单好用!源码揭秘! 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. 前言

大家好,我是若川。最近組織了源碼共讀活動,感興趣的可以加我微信 ruochuan12

想學源碼,極力推薦之前我寫的《學習源碼整體架構系列》jQuery、underscore、lodash、vuex、sentry、axios、redux、koa、vue-devtools、vuex4、koa-compose、vue-next-release、vue-this、create-vue等十余篇源碼文章。

本文倉庫 ni-analysis,求個star^_^[1]

最近組織了源碼共讀活動

之前寫了 Vue3 相關的兩篇文章。

  • 初學者也能看懂的 Vue3 源碼中那些實用的基礎工具函數

  • Vue 3.2 發布了,那尤雨溪是怎么發布 Vue.js 的?

文章里都是寫的使用 yarn 。參加源碼共讀的小伙伴按照我的文章,卻拉取的最新倉庫代碼,發現 yarn install 安裝不了依賴,向我反饋報錯。于是我去 github倉庫 一看,發現尤雨溪把 Vue3倉庫 從 yarn 換成了 `pnpm`[2]。貢獻文檔[3]中有一句話。

We also recommend installing ni[4] to help switching between repos using different package managers. ni also provides the handy nr command which running npm scripts easier.

我們還建議安裝 ni[5] 以幫助使用不同的包管理器在 repos 之間切換。ni 還提供了方便的 nr 命令,可以更輕松地運行 npm 腳本。

這個 ni 項目源碼雖然是 ts,沒用過 ts 小伙伴也是很好理解的,而且主文件其實不到 100行,非常適合我們學習。

閱讀本文,你將學到:

1.?學會?ni?使用和理解其原理 2.?學會調試學習源碼 3.?可以在日常工作中也使用?ni 4.?等等

2. 原理

github 倉庫 ni#how[6]

ni 假設您使用鎖文件(并且您應該)

在它運行之前,它會檢測你的 yarn.lock / pnpm-lock.yaml / package-lock.json 以了解當前的包管理器,并運行相應的命令。

單從這句話中可能有些不好理解,還是不知道它是個什么。我解釋一下。

使用?`ni`?在項目中安裝依賴時:假設你的項目中有鎖文件?`yarn.lock`,那么它最終會執行?`yarn install`?命令。假設你的項目中有鎖文件?`pnpm-lock.yaml`,那么它最終會執行?`pnpm i`?命令。假設你的項目中有鎖文件?`package-lock.json`,那么它最終會執行?`npm i`?命令。使用?`ni?-g?vue-cli`?安裝全局依賴時默認使用?`npm?i?-g?vue-cli`當然不只有?`ni`?安裝依賴。還有?`nr`?-?run`nx`?-?execute`nu`?-?upgrade`nci`?-?clean?install`nrm`?-?remove

我看源碼發現:ni相關的命令,都可以在末尾追加\?,表示只打印,不是真正執行

所以全局安裝 ni 后,可以盡情測試,比如 ni \?,nr dev --port=3000 \?,因為打印,所以可以在各種目錄下執行,有助于理解 ni 源碼。我測試了如下圖所示:

命令測試圖示

假設項目目錄下沒有鎖文件,默認就會讓用戶從npm、yarn、pnpm選擇,然后執行相應的命令。但如果在~/.nirc文件中,設置了全局默認的配置,則使用默認配置執行對應命令。

Config

; ~/.nirc; fallback when no lock found defaultAgent=npm # default "prompt"; for global installs globalAgent=npm

因此,我們可以得知這個工具必然要做三件事

1.?根據鎖文件猜測用哪個包管理器?npm/yarn/pnpm? 2.?抹平不同的包管理器的命令差異 3.?最終運行相應的腳本

接著繼續看看 README 其他命令的使用,就會好理解。

3. 使用

看 ni github文檔[7]

npm i in a yarn project, again? F**k!

ni - use the right package manager

全局安裝。

npm?i?-g?@antfu/ni

如果全局安裝遭遇沖突,我們可以加上 --force 參數強制安裝。

舉幾個常用的例子。

3.1 ni - install

ni#?npm?install #?yarn?install #?pnpm?installni?axios#?npm?i?axios #?yarn?add?axios #?pnpm?i?axios

3.2 nr - run

nr?dev?--port=3000#?npm?run?dev?--?--port=3000 #?yarn?run?dev?--port=3000 #?pnpm?run?dev?--?--port=3000nr #?交互式選擇命令去執行 #?interactively?select?the?script?to?run #?supports?https://www.npmjs.com/package/npm-scripts-info?conventionnr?-#?重新執行最后一次執行的命令 #?rerun?the?last?command

3.3 nx - execute

nx?jest#?npx?jest #?yarn?dlx?jest #?pnpm?dlx?jest

4. 閱讀源碼前的準備工作

4.1 克隆

#?推薦克隆我的倉庫(我的保證對應文章版本) git?clone?https://github.com/lxchuan12/ni-analysis.git cd?ni-analysis/ni #?npm?i?-g?pnpm #?安裝依賴 pnpm?i #?當然也可以直接用?ni#?或者克隆官方倉庫 git?clone?https://github.com/vuejs/ni.git cd?ni #?npm?i?-g?pnpm #?安裝依賴 pnpm?i #?當然也可以直接用?ni

眾所周知,看一個開源項目,先從 package.json 文件開始看起。

4.2 package.json 文件

{"name":?"@antfu/ni","version":?"0.10.0","description":?"Use?the?right?package?manager",//?暴露了六個命令"bin":?{"ni":?"bin/ni.js","nci":?"bin/nci.js","nr":?"bin/nr.js","nu":?"bin/nu.js","nx":?"bin/nx.js","nrm":?"bin/nrm.js"},"scripts":?{//?省略了其他的命令?用?esno?執行?ts?文件//?可以加上???便于調試,也可以不加//?或者是終端?npm?run?dev?\?"dev":?"esno?src/ni.ts??"}, }

根據 dev 命令,我們找到主入口文件 src/ni.ts。

4.3 從源碼主入口開始調試

//?ni/src/ni.ts import?{?parseNi?}?from?'./commands' import?{?runCli?}?from?'./runner'//?我們可以在這里斷點 runCli(parseNi)

找到 ni/package.json 的 scripts,把鼠標移動到 dev 命令上,會出現運行腳本和調試腳本命令。如下圖所示,選擇調試腳本。

VSCode 調試VSCode 調試 Node.js 說明

5. 主流程 runner - runCli 函數

這個函數就是對終端傳入的命令行參數做一次解析。最終還是執行的 run 函數。

對于 process 不了解的讀者,可以看阮一峰老師寫的 process 對象[8]

//?ni/src/runner.ts export?async?function?runCli(fn:?Runner,?options:?DetectOptions?=?{})?{// process.argv:返回一個數組,成員是當前進程的所有命令行參數。//?其中 process.argv 的第一和第二個元素是Node可執行文件和被執行JavaScript文件的完全限定的文件系統路徑,無論你是否這樣輸入他們。const?args?=?process.argv.slice(2).filter(Boolean)try?{await?run(fn,?args,?options)}catch?(error)?{// process.exit方法用來退出當前進程。它可以接受一個數值參數,如果參數大于0,表示執行失敗;如果等于0表示執行成功。process.exit(1)} }

我們接著來看,run 函數。

6. 主流程 runner - run 主函數

這個函數主要做了三件事

1.?根據鎖文件猜測用哪個包管理器?npm/yarn/pnpm?-?detect?函數 2.?抹平不同的包管理器的命令差異?-?parseNi?函數 3.?最終運行相應的腳本?-?execa?工具//?ni/src/runner.ts //?源碼有刪減 import?execa?from?'execa' const?DEBUG_SIGN?=?'?' export?async?function?run(fn:?Runner,?args:?string[],?options:?DetectOptions?=?{})?{//?命令參數包含?問號??則是調試模式,不執行腳本const?debug?=?args.includes(DEBUG_SIGN)if?(debug)//?調試模式下,刪除這個問號remove(args,?DEBUG_SIGN)//?cwd?方法返回進程的當前目錄(絕對路徑)let?cwd?=?process.cwd()let?command//?支持指定?文件目錄//?ni?-C?packages/foo?vite//?nr?-C?playground?devif?(args[0]?===?'-C')?{cwd?=?resolve(cwd,?args[1])//?刪掉這兩個參數?-C?packages/fooargs.splice(0,?2)}//?如果是全局安裝,那么實用全局的包管理器const?isGlobal?=?args.includes('-g')if?(isGlobal)?{command?=?await?fn(getGlobalAgent(),?args)}else?{let?agent?=?await?detect({?...options,?cwd?})?||?getDefaultAgent()//?猜測使用哪個包管理器,如果沒有發現鎖文件,會返回?null,則調用?getDefaultAgent?函數,默認返回是讓用戶選擇?promptif?(agent?===?'prompt')?{agent?=?(await?prompts({name:?'agent',type:?'select',message:?'Choose?the?agent',choices:?agents.map(value?=>?({?title:?value,?value?})),})).agentif?(!agent)return}//?這里的?fn?是?傳入解析代碼的函數command?=?await?fn(agent?as?Agent,?args,?{hasLock:?Boolean(agent),cwd,})}//?如果沒有命令,直接返回,上一個?runCli?函數報錯,退出進程if?(!command)return//?如果是調試模式,那么直接打印出命令。調試非常有用。if?(debug)?{//?eslint-disable-next-line?no-consoleconsole.log(command)return}//?最終用?execa?執行命令,比如?npm?i//?https://github.com/sindresorhus/execa//?介紹:Process execution for humansawait?execa.command(command,?{?stdio:?'inherit',?encoding:?'utf-8',?cwd?}) }

我們學習完主流程,接著來看兩個重要的函數:detect 函數、parseNi 函數。

根據入口我們可以知道。

runCli(parseNi)run(fn)這里?fn?則是?parseNi

6.1 根據鎖文件猜測用哪個包管理器(npm/yarn/pnpm) - detect 函數

代碼相對不多,我就全部放出來了。

主要就做了三件事情1. 找到項目根路徑下的鎖文件。返回對應的包管理器?`npm/yarn/pnpm`。 2. 如果沒找到,那就返回?`null`。 3. 如果找到了,但是用戶電腦沒有這個命令,則詢問用戶是否自動安裝。//?ni/src/agents.ts export?const?LOCKS:?Record<string,?Agent>?=?{'pnpm-lock.yaml':?'pnpm','yarn.lock':?'yarn','package-lock.json':?'npm', }//?ni/src/detect.ts export?async?function?detect({?autoInstall,?cwd?}:?DetectOptions)?{const?result?=?await?findUp(Object.keys(LOCKS),?{?cwd?})const?agent?=?(result???LOCKS[path.basename(result)]?:?null)if?(agent?&&?!cmdExists(agent))?{if?(!autoInstall)?{console.warn(`Detected?${agent}?but?it?doesn't?seem?to?be?installed.\n`)if?(process.env.CI)process.exit(1)const?link?=?terminalLink(agent,?INSTALL_PAGE[agent])const?{?tryInstall?}?=?await?prompts({name:?'tryInstall',type:?'confirm',message:?`Would?you?like?to?globally?install?${link}?`,})if?(!tryInstall)process.exit(1)}await?execa.command(`npm?i?-g?${agent}`,?{?stdio:?'inherit',?cwd?})}return?agent }

接著我們來看 parseNi 函數。

6.2 抹平不同的包管理器的命令差異 - parseNi 函數

//?ni/src/commands.ts export?const?parseNi?=?<Runner>((agent,?args,?ctx)?=>?{//?ni?-v?輸出版本號if?(args.length?===?1?&&?args[0]?===?'-v')?{//?eslint-disable-next-line?no-consoleconsole.log(`@antfu/ni?v${version}`)process.exit(0)}if?(args.length?===?0)return?getCommand(agent,?'install')//?省略一些代碼 })

通過 getCommand 獲取命令。

//?ni/src/agents.ts //?有刪減 //?一份配置,寫個這三種包管理器中的命令。export?const?AGENTS?=?{npm:?{'install':?'npm?i'},yarn:?{'install':?'yarn?install'},pnpm:?{'install':?'pnpm?i'}, }//?ni/src/commands.ts export?function?getCommand(agent:?Agent,command:?Command,args:?string[]?=?[], )?{//?包管理器不在?AGENTS?中則報錯//?比如?npm?不在if?(!(agent?in?AGENTS))throw?new?Error(`Unsupported?agent?"${agent}"`)//?獲取命令?安裝則對應?npm?installconst?c?=?AGENTS[agent][command]//?如果是函數,則執行函數。if?(typeof?c?===?'function')return?c(args)//?命令?沒找到,則報錯if?(!c)throw?new?Error(`Command?"${command}"?is?not?support?by?agent?"${agent}"`)//?最終拼接成命令字符串return?c.replace('{0}',?args.join('?')).trim() }

6.3 最終運行相應的腳本

得到相應的命令,比如是 npm i,最終用這個工具 execa[9] 執行最終得到的相應的腳本。

await?execa.command(command,?{?stdio:?'inherit',?encoding:?'utf-8',?cwd?})

7. 總結

我們看完源碼,可以知道這個神器 ni 主要做了三件事

1.?根據鎖文件猜測用哪個包管理器?npm/yarn/pnpm?-?detect?函數 2.?抹平不同的包管理器的命令差異?-?parseNi?函數 3.?最終運行相應的腳本?-?execa?工具

我們日常開發中,可能容易 npm、yarn、pnpm 混用。有了 ni 后,可以用于日常開發使用。Vue 核心成員 Anthony Fu[10] 發現問題,最終開發了一個工具 ni[11] 解決問題。而這種發現問題、解決問題的能力正是我們前端開發工程師所需要的。

另外,我發現 Vue 生態很多基本都切換成了使用 pnpm[12]

因為文章不宜過長,所以未全面展開講述源碼中所有細節。非常建議讀者朋友按照文中方法使用VSCode調試 ni 源碼。學會調試源碼后,源碼并沒有想象中的那么難

最后可以持續關注我@若川。歡迎加我微信 ruochuan12源碼共讀 活動,大家一起學習源碼,共同進步。

參考資料

[1]

本文倉庫 ni-analysis,求個star^_^: https://github.com/lxchuan12/ni-analysis.git

[2]

pnpm: https://github.com/vuejs/vue-next/pull/4766/files

[3]

貢獻文檔: https://github.com/vuejs/vue-next/blob/master/.github/contributing.md#development-setup

[4]

ni: https://github.com/antfu/ni

[5]

ni: https://github.com/antfu/ni

[6]

github 倉庫 ni#how: https://github.com/antfu/ni#how

[7]

ni github文檔: https://github.com/antfu/ni

[8]

阮一峰老師寫的 process 對象: http://javascript.ruanyifeng.com/nodejs/process.html

[9]

execa: https://github.com/sindresorhus/execa

[10]

Anthony Fu: https://antfu.me

[11]

ni: https://github.com/antfu/ni

[12]

pnpm: https://pnpm.io

最近組建了一個江西人的前端交流群,如果你是江西人可以加我微信?ruochuan12?私信 江西?拉你進群。

推薦閱讀

1個月,200+人,一起讀了4周源碼
我歷時3年才寫了10余篇源碼文章,但收獲了100w+閱讀

老姚淺談:怎么學JavaScript?

我在阿里招前端,該怎么幫你(可進面試群)

·················?若川簡介?·················

你好,我是若川,畢業于江西高校。現在是一名前端開發“工程師”。寫有《學習源碼整體架構系列
從2014年起,每年都會寫一篇年度總結,已經寫了7篇,點擊查看年度總結。
同時,最近組織了源碼共讀活動

識別方二維碼加我微信、拉你進源碼共讀

今日話題

略。歡迎分享、收藏、點贊、在看我的公眾號文章~

總結

以上是生活随笔為你收集整理的尤雨溪推荐神器 ni ,能替代 npm/yarn/pnpm ?简单好用!源码揭秘!的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。