庫(kù)完整實(shí)踐)
我做了幾年Node.js開(kāi)發(fā)和前端基建每次換電腦、換項(xiàng)目最頭疼的事情往往不是寫(xiě)代碼而是等依賴(lài)。公司網(wǎng)絡(luò)訪(fǎng)問(wèn)npm官方源時(shí)斷時(shí)續(xù)裝一個(gè)依賴(lài)動(dòng)輒幾分鐘內(nèi)網(wǎng)機(jī)更慘干脆連不上。后來(lái)我花了一個(gè)周末在Windows Server上搭了一套 npm 私有倉(cāng)庫(kù)跑到現(xiàn)在一年半從團(tuán)隊(duì)幾個(gè)人用到幾十人沒(méi)出過(guò)大亂子。整個(gè)方案核心就兩個(gè)工具Verdaccio提供倉(cāng)庫(kù)服務(wù)PM2負(fù)責(zé)讓它在Windows上常駐并開(kāi)機(jī)自啟。這篇文章寫(xiě)給想在Windows環(huán)境里自建npm私有倉(cāng)庫(kù)的人不管你是運(yùn)維、前端負(fù)責(zé)人還是一個(gè)想給本地開(kāi)發(fā)做依賴(lài)緩存的獨(dú)立開(kāi)發(fā)者下面這套流程基本可以照著走。我會(huì)把環(huán)境配置、Verdaccio的核心配置、PM2進(jìn)程管理、客戶(hù)端發(fā)布與安裝全部過(guò)一遍全程以Windows為主線(xiàn)把容易踩的坑提前標(biāo)出來(lái)。1. 為什么自建npm私有倉(cāng)庫(kù)以及我為什么沒(méi)選Nexus1.1 先認(rèn)清三個(gè)真實(shí)場(chǎng)景第一個(gè)場(chǎng)景內(nèi)網(wǎng)隔離。公司研發(fā)網(wǎng)段只能訪(fǎng)問(wèn)白名單域名公網(wǎng)npm registry根本不在白名單里。這種情況不做私有倉(cāng)庫(kù)就只能靠U盤(pán)搬運(yùn)node_modules非常痛苦。第二個(gè)場(chǎng)景外網(wǎng)能通但很慢。開(kāi)發(fā)機(jī)多、依賴(lài)多每次從零npm ci都要等十幾分鐘。私有倉(cāng)庫(kù)會(huì)在本地緩存所有下載過(guò)的包之后再有安裝請(qǐng)求直接走局域網(wǎng)速度從分鐘級(jí)降到秒級(jí)。第三個(gè)場(chǎng)景要發(fā)公司內(nèi)部包。公共組件庫(kù)、工具函數(shù)庫(kù)不想發(fā)到公網(wǎng)npm又希望安裝時(shí)跟普通npm包一樣簡(jiǎn)單這種需求光靠Git鏈接分發(fā)不好維護(hù)版本。這三點(diǎn)合在一起就是自建npm私有倉(cāng)庫(kù)的核心價(jià)值緩存加速、統(tǒng)一管控、私域發(fā)布。1.2 方案對(duì)比Verdaccio、Nexus還是CNPM當(dāng)時(shí)我調(diào)研過(guò)三個(gè)方向Nexus Repository Manager功能全支持Maven、npm、Docker鏡像等一大堆格式但配置復(fù)雜內(nèi)存占用高搭一個(gè)東西要理解倉(cāng)庫(kù)組、Blob存儲(chǔ)、權(quán)限矩陣運(yùn)維成本不低。CNPM基于淘寶開(kāi)源的cnpmjs.org思路適合服務(wù)大規(guī)模團(tuán)隊(duì)但架構(gòu)里包含數(shù)據(jù)庫(kù)、多進(jìn)程調(diào)度單獨(dú)為幾十人的前端團(tuán)隊(duì)上這一套有點(diǎn)重。Verdaccio純Node.js實(shí)現(xiàn)單一進(jìn)程配置文件是一個(gè)YAML默認(rèn)端口4873自帶Web界面。官方直接支持Windows下運(yùn)行安裝只需一行npm i -g verdaccio。我最后選Verdaccio還有一個(gè)重要原因是團(tuán)隊(duì)本身就在Node.js生態(tài)里出問(wèn)題可以自己維護(hù)不用去學(xué)Java系的倉(cāng)庫(kù)管理概念。Verdaccio的緩存機(jī)制是“按需代理”——本地沒(méi)有的包它從上游registry下載一份存起來(lái)之后同版本不再回源。這個(gè)機(jī)制對(duì)開(kāi)發(fā)場(chǎng)景非常友好。1.3 私有倉(cāng)庫(kù)搭好之后日常工作流變成什么樣搭好之后團(tuán)隊(duì)成員的npm install不再直連公網(wǎng)而是先請(qǐng)求Verdaccio由它決定是直接給緩存還是回源拉取。發(fā)布私有包時(shí)包名使用 scoped 形式比如team/component走npm publish推到本地倉(cāng)庫(kù)。整個(gè)流程對(duì)開(kāi)發(fā)者幾乎透明只需要把registry改一次。這樣做的額外好處是依賴(lài)版本出現(xiàn)問(wèn)題時(shí)可以登錄Verdaccio的Web界面查看包列表、版本歷史和md5校驗(yàn)值排查問(wèn)題比對(duì)著node_modules翻快得多。因?yàn)樗邪惭b流量都經(jīng)過(guò)倉(cāng)庫(kù)我還能從訪(fǎng)問(wèn)日志里看出哪些包被頻繁拉取哪些版本一直沒(méi)人用后面清理依賴(lài)的時(shí)候心里有數(shù)。2. 在Windows上先把npm環(huán)境理順PATH、PowerShell執(zhí)行策略與鏡像源2.1 “npm不是內(nèi)部或外部命令”的根因在Windows上裝好Node.js之后很多人會(huì)遇到npm 不是內(nèi)部或外部命令。這通常不是npm沒(méi)裝上而是Node.js的安裝目錄沒(méi)有寫(xiě)進(jìn)PATH或者安裝過(guò)程中環(huán)境變量刷新不及時(shí)。判斷方法很簡(jiǎn)單打開(kāi)cmd輸入where npm where node如果where能列出來(lái)說(shuō)明PATH里有如果提示找不到就把Node.js安裝目錄比如C:\Program Files\nodejs\手動(dòng)加到系統(tǒng)環(huán)境變量的Path中。改完之后一定要重新打開(kāi)終端窗口因?yàn)閃indows終端的環(huán)境變量是在創(chuàng)建時(shí)快照的老窗口不會(huì)自動(dòng)感知新變量。還有一個(gè)隱藏問(wèn)題如果你電腦里裝過(guò)多個(gè)Node.js版本比如一個(gè)來(lái)自官方安裝包一個(gè)來(lái)自nvm-windowsPATH里出現(xiàn)兩條Node路徑npm命令可能指向你沒(méi)想到的那個(gè)版本。遇到這種情況我建議統(tǒng)一用nvm-windows管理Node版本PATH里只留nvm的入口避免系統(tǒng)自帶Node和實(shí)際使用的Node打架。2.2 PowerShell下 npm.ps1 禁止運(yùn)行腳本的問(wèn)題這個(gè)坑我在Windows上遇到太多次了。在PowerShell里執(zhí)行npm install突然報(bào)npm : 無(wú)法加載文件 C:\Program Files\nodejs\npm.ps1因?yàn)樵诖讼到y(tǒng)上禁止運(yùn)行腳本。原因是PowerShell的執(zhí)行策略默認(rèn)是Restricted禁止運(yùn)行任何.ps1腳本。npm在Windows下有一個(gè)npm.ps1入口因?yàn)镻owerShell的環(huán)境被策略限制腳本直接被拒了。修復(fù)方法并不復(fù)雜用一個(gè)管理員權(quán)限的PowerShell窗口執(zhí)行Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUserRemoteSigned的意思是本地腳本可以運(yùn)行從網(wǎng)上下載的腳本必須有可信簽名。設(shè)置完成后再用Get-ExecutionPolicy確認(rèn)結(jié)果。如果不想用PowerShell也可以在cmd里跑npmcmd不受PowerShell執(zhí)行策略限制但治標(biāo)不治本團(tuán)隊(duì)里如果有人用VS Code的默認(rèn)終端照樣會(huì)遇到。我第一次搭Verdaccio的時(shí)候就是被這個(gè)策略卡了一晚上。cmd窗口里一切正常切到Windows Terminal用PowerShell掃一眼服務(wù)狀態(tài)順手執(zhí)行個(gè)npm命令就報(bào)腳本禁運(yùn)。后來(lái)把執(zhí)行策略改掉這類(lèi)問(wèn)題才徹底消失。2.3 npm鏡像源和私有倉(cāng)庫(kù)的關(guān)系很多人一開(kāi)始為了加速安裝會(huì)把全站registry切到國(guó)內(nèi)鏡像npm config set registry https://registry.npmmirror.com這個(gè)做法對(duì)個(gè)人開(kāi)發(fā)沒(méi)問(wèn)題但在團(tuán)隊(duì)場(chǎng)景里每個(gè)人各用各的鏡像很難保證版本一致性。私有倉(cāng)庫(kù)的價(jià)值之一就是把“鏡像源”收攏到團(tuán)隊(duì)內(nèi)部把上游選擇權(quán)統(tǒng)一控制。Verdaccio的配置里可以有多個(gè)uplinks比如上游用npm官方源或者用npmmirror。團(tuán)隊(duì)內(nèi)部所有機(jī)器只認(rèn)本地倉(cāng)庫(kù)地址至于本地倉(cāng)庫(kù)回源時(shí)走哪個(gè)上游由管理員在服務(wù)端決定。這樣既享受了國(guó)內(nèi)鏡像的快又避免了開(kāi)發(fā)機(jī)各搞一套的混亂。這里還要提醒一點(diǎn)配置鏡像源時(shí)新舊npm對(duì)registry的緩存策略不太一樣。如果發(fā)現(xiàn)改了registry之后安裝還是走了舊地址檢查一下npm config get registry的實(shí)際值以及項(xiàng)目根的.npmrc是否有殘留配置。.npmrc的優(yōu)先級(jí)比全局配置高很多人全局改了沒(méi)用就是被項(xiàng)目級(jí)文件覆蓋了。3. Verdaccio配置文件的每一段我都是怎么填的3.1 安裝、初始化與配置文件位置安裝很簡(jiǎn)單npm i -g verdaccio安裝之后先啟動(dòng)一次verdaccio第一次啟動(dòng)會(huì)自動(dòng)在用戶(hù)目錄下生成配置目錄。Windows上是C:\Users\用戶(hù)名\AppData\Roaming\verdaccio\里面主要有兩個(gè)東西config.yaml是配置文件storage目錄是包的實(shí)際存儲(chǔ)位置。默認(rèn)配置可以直接跑但要做團(tuán)隊(duì)私服至少要把監(jiān)聽(tīng)地址、認(rèn)證、權(quán)限、上游代理這幾項(xiàng)改明白。我建議一開(kāi)始就編輯config.yaml而不是直接裸跑省得后面服務(wù)起來(lái)了又要重啟。3.2 一份適合內(nèi)網(wǎng)Windows場(chǎng)景的完整config.yaml下面這份配置我實(shí)際用于團(tuán)隊(duì)內(nèi)網(wǎng)環(huán)境錨點(diǎn)都在Windows路徑下可以直接參考storage: C:/Users/yourname/AppData/Roaming/verdaccio/storage auth: htpasswd: file: C:/Users/yourname/AppData/Roaming/verdaccio/htpasswd max_users: 200 web: enable: true title: Team NPM Private Repository primary_color: #2d6cdf uplinks: npmmirror: url: https://registry.npmmirror.com/ timeout: 60s max_fails: 5 npmjs: url: https://registry.npmjs.org/ timeout: 30s packages: team/*: access: $all publish: $authenticated proxy: npmmirror npmjs **: access: $all publish: $authenticated proxy: npmmirror npmjs listen: - 0.0.0.0:4873 log: type: stdout format: pretty level: http解釋幾個(gè)關(guān)鍵點(diǎn)storage指定包存儲(chǔ)目錄建議放到數(shù)據(jù)盤(pán)后面?zhèn)浞輹r(shí)直接拷這個(gè)目錄。auth.htpasswd.file是賬號(hào)文件Verdaccio用它來(lái)做基本認(rèn)證。max_users: 200表示允許通過(guò)Web界面或npm adduser自助注冊(cè)賬號(hào)。如果完全不想開(kāi)放注冊(cè)改成max_users: -1賬號(hào)只能由管理員手動(dòng)在文件里維護(hù)。uplinks是上游源列表。我通常配置兩個(gè)npmmirror作為默認(rèn)回源npm官方作為備用。注意proxy順序有講究列表里第一個(gè)是優(yōu)先選擇。packages是權(quán)限規(guī)則的核心。team/*代表公司私有作用域包access: $all表示所有人能下載publish: $authenticated表示只有登錄用戶(hù)才能發(fā)布。**是兜底規(guī)則匹配所有其他包。listen一定要寫(xiě)0.0.0.0:4873否則Verdaccio可能只監(jiān)聽(tīng)127.0.0.1團(tuán)隊(duì)其他機(jī)器無(wú)法訪(fǎng)問(wèn)。3.3 權(quán)限模型與uplinks的配合邏輯Verdaccio的權(quán)限模型一共就三個(gè)角色$all所有人、$anonymous未登錄用戶(hù)、$authenticated已登錄用戶(hù)。理解這三個(gè)角色之后配置就不難了。我的做法是下載默認(rèn)開(kāi)放給所有人發(fā)布只開(kāi)放給登錄用戶(hù)。因?yàn)樗接袀}(cāng)庫(kù)的價(jià)值是讓安裝無(wú)障礙而發(fā)布關(guān)系到代碼安全。如果某些包希望完全內(nèi)網(wǎng)不代理外網(wǎng)可以把對(duì)應(yīng)的proxy去掉packages: team/private-core: access: $authenticated publish: $authenticated去掉proxy后這個(gè)包如果本地沒(méi)有安裝會(huì)直接報(bào)錯(cuò)不會(huì)回源。適合那種明確只允許內(nèi)網(wǎng)使用的核心依賴(lài)。uplinks和proxy的配合還需要注意如果團(tuán)隊(duì)所在網(wǎng)絡(luò)完全上不了外網(wǎng)那么uplinks可以保留配置但不讓它真的生效或者直接刪掉。因?yàn)橐坏┠硞€(gè)包在本地緩存中沒(méi)有而uplink又連不上安裝會(huì)卡在上游請(qǐng)求超時(shí)。離線(xiàn)場(chǎng)景下我更建議把proxy全去掉只允許安裝本地已經(jīng)存在的包這樣至少不會(huì)讓開(kāi)發(fā)機(jī)等半天才報(bào)錯(cuò)。3.4 修改配置后如何生效修改config.yaml后最簡(jiǎn)單的方式是重啟進(jìn)程。用PM2管理后我一般是pm2 restart verdaccio如果是純前臺(tái)方式運(yùn)行可以CtrlC停掉再重新執(zhí)行verdaccio。Verdaccio對(duì)配置文件的監(jiān)聽(tīng)并不是百分百可靠的別指望改了配置它自動(dòng)熱加載所有字段。改一次配置重啟一次養(yǎng)成習(xí)慣。另外Windows上如果用了殺毒軟件或安全策略可能會(huì)把Verdaccio的storage目錄盯得很緊導(dǎo)致讀寫(xiě)變慢甚至權(quán)限拒絕。我習(xí)慣把storage配置在單獨(dú)的目錄并把該目錄加入殺毒白名單實(shí)測(cè)能避免不少莫名其妙的“包寫(xiě)不進(jìn)去”問(wèn)題。4. 用PM2讓Verdaccio常駐Windows兼談開(kāi)機(jī)自啟的正確姿勢(shì)4.1 為什么不用cmd窗口裸跑也不用NSSMVerdaccio官方文檔推薦直接verdaccio前臺(tái)運(yùn)行Windows上你確實(shí)可以這么做但有個(gè)致命問(wèn)題一個(gè)cmd窗口開(kāi)著服務(wù)就在誰(shuí)不小心關(guān)掉窗口倉(cāng)庫(kù)就沒(méi)了。團(tuán)隊(duì)的人找你排查依賴(lài)問(wèn)題你跑過(guò)去一看原來(lái)是運(yùn)維那條窗口被回收了非常尷尬。有人會(huì)想到NSSMNon-Sucking Service Manager它可以把任意exe注冊(cè)成Windows服務(wù)。但NSSM方案在Verdaccio這種需要讀取環(huán)境變量的Node服務(wù)上配置起來(lái)有點(diǎn)繞。相比之下PM2在Node.js圈子里更通用日志、重啟、監(jiān)控、進(jìn)程列表這些都用一套命令搞定。而且PM2本身也是Node生態(tài)的工具團(tuán)隊(duì)里的人上手門(mén)檻低。4.2 PM2啟動(dòng)Verdaccio的具體操作全局安裝PM2npm i -g pm2啟動(dòng)Verdaccio注意第一次仍然需要先手動(dòng)確認(rèn)Verdaccio能跑再交給PM2pm2 start verdaccio --name verdaccio啟動(dòng)后查看狀態(tài)pm2 list如果Verdaccio啟動(dòng)出問(wèn)題看日志pm2 logs verdaccio --lines 50日常運(yùn)維中我用到最多的幾個(gè)命令pm2 restart verdaccio重啟服務(wù)改配置后用。pm2 stop verdaccio/pm2 start verdaccio臨時(shí)停啟。pm2 monit查看CPU、內(nèi)存。pm2 save保存當(dāng)前進(jìn)程列表配合開(kāi)機(jī)恢復(fù)使用。在Windows上還有一個(gè)小細(xì)節(jié)PM2管理的進(jìn)程是通過(guò)node.exe運(yùn)行的但pm2 list里顯示的name就是你在--name參數(shù)里指定的名字方便識(shí)別。別同時(shí)開(kāi)好幾個(gè)前端項(xiàng)目最后分不清哪個(gè)是倉(cāng)庫(kù)服務(wù)。還有一個(gè)容易被忽略的點(diǎn)PM2會(huì)默認(rèn)開(kāi)啟“watch”嗎不會(huì)。如果你想讓Verdaccio配置改動(dòng)后自動(dòng)重啟需要額外加--watch參數(shù)。但我不建議在生產(chǎn)倉(cāng)庫(kù)上開(kāi)watch因?yàn)閏onfig.yaml被編輯時(shí)如果語(yǔ)法有問(wèn)題watch模式會(huì)導(dǎo)致進(jìn)程反復(fù)重啟。我寧愿手動(dòng)pm2 restart安全可控。4.3 Windows開(kāi)機(jī)自啟pm2 startup不可用怎么辦Linux上執(zhí)行pm2 startup就能用systemd做開(kāi)機(jī)自啟但Windows不支持這個(gè)命令。我自己第一次試的時(shí)候執(zhí)行后明明提示成功重啟機(jī)器發(fā)現(xiàn)服務(wù)還是沒(méi)起來(lái)。后來(lái)才確認(rèn)PM2官方在Windows的啟動(dòng)支持依賴(lài)額外的服務(wù)包裝工具。我推薦用pm2-windows-servicenpm i -g pm2-windows-service然后在管理員權(quán)限的PowerShell窗口執(zhí)行服務(wù)安裝pm2-service-install -n pm2-service -u 你的Windows用戶(hù)名 -p 你的密碼安裝完成后Windows服務(wù)管理器里會(huì)出現(xiàn)一個(gè)名為pm2-service的服務(wù)開(kāi)機(jī)自動(dòng)啟動(dòng)。之后再用pm2 start verdaccio啟動(dòng)倉(cāng)庫(kù)并且執(zhí)行pm2 save就能實(shí)現(xiàn)開(kāi)機(jī)后由PM2自動(dòng)拉起Verdaccio。需要注意幾點(diǎn)安裝服務(wù)時(shí)用的Windows賬號(hào)必須有相應(yīng)權(quán)限如果用SYSTEM賬號(hào)方式PM2可能讀不到用戶(hù)目錄下的verdaccio配置導(dǎo)致啟動(dòng)失敗。我是直接指定了日常使用的管理員賬號(hào)實(shí)測(cè)最穩(wěn)。如果公司安全策略不允許把明文密碼寫(xiě)在命令行參數(shù)里考慮改用pm2-installer但后者需要額外準(zhǔn)備Visual Studio Build Tools和node-gyp環(huán)境安裝成本高。我試過(guò)一次覺(jué)得沒(méi)有pm2-windows-service省心。卸載服務(wù)用pm2-service-uninstall別手動(dòng)去services.msc里刪容易留下半殘狀態(tài)。執(zhí)行服務(wù)安裝前先確認(rèn)Verdaccio已經(jīng)通過(guò)pm2 start托管并且執(zhí)行過(guò)pm2 save。服務(wù)只是把PM2的守護(hù)進(jìn)程拉起來(lái)真正的恢復(fù)動(dòng)作靠的是PM2自己保存的進(jìn)程列表。如果你不想裝額外的npm包Windows計(jì)劃任務(wù)也可以實(shí)現(xiàn)開(kāi)機(jī)自啟。先pm2 save然后在計(jì)劃任務(wù)里創(chuàng)建一個(gè)開(kāi)機(jī)觸發(fā)任務(wù)執(zhí)行的程序是cmd參數(shù)是/c pm2 resurrect。這個(gè)方法我實(shí)測(cè)過(guò)能用但計(jì)劃任務(wù)在系統(tǒng)剛剛啟動(dòng)、網(wǎng)絡(luò)還沒(méi)有完全就緒時(shí)可能會(huì)失敗Verdaccio需要稍后手動(dòng)拉起。相比起來(lái)pm2-windows-service處理時(shí)序更穩(wěn)定。4.4 端口占用排查4873被別的進(jìn)程搶了怎么辦Windows上啟動(dòng)Verdaccio最經(jīng)典的問(wèn)題就是端口被占。查端口占用netstat -ano | findstr :4873如果結(jié)果里有進(jìn)程按PID殺掉taskkill /F /PID 1234但要注意殺進(jìn)程只是緩解。如果某個(gè)程序總是在開(kāi)機(jī)時(shí)搶4873最好在config.yaml里換一個(gè)端口然后把客戶(hù)端registry地址同步改掉。改端口不是一個(gè)難事但團(tuán)隊(duì)成員都要跟著改所以建議一開(kāi)始就規(guī)劃好端口比如4873、4874都行盡量避開(kāi)Windows上常見(jiàn)的開(kāi)發(fā)工具端口。5. 客戶(hù)端接入與發(fā)布私有包附一份常見(jiàn)報(bào)錯(cuò)排查表5.1 把開(kāi)發(fā)機(jī)的registry指到私有倉(cāng)庫(kù)最簡(jiǎn)單的方式npm config set registry http://服務(wù)器IP:4873如果團(tuán)隊(duì)里有人不想全局改registry可以只在項(xiàng)目里配。項(xiàng)目根目錄創(chuàng)建.npmrc寫(xiě)入registryhttp://服務(wù)器IP:4873.npmrc的優(yōu)先級(jí)是項(xiàng)目級(jí) 用戶(hù)級(jí) 全局所以項(xiàng)目配了自己生效不影響其他項(xiàng)目。我更推薦的是用nrm管理多個(gè)源npm i -g nrm nrm add local http://服務(wù)器IP:4873 nrm use local這樣切換官方源、公司源、鏡像源都方便。團(tuán)隊(duì)協(xié)作時(shí)我一般會(huì)把地址直接固化到文檔或腳手架模板里新人拉代碼后執(zhí)行一次nrm use local就完成了。這里有個(gè)細(xì)節(jié)要注意如果Verdaccio服務(wù)器用的不是默認(rèn)4873端口而是自定義端口客戶(hù)端配置時(shí)一定要寫(xiě)全端口號(hào)別省略。很多人瀏覽器訪(fǎng)問(wèn)http://192.168.1.10:4873能打開(kāi)Web界面但npm install一直連不上一看是.npmrc里漏寫(xiě)了端口。5.2 注冊(cè)賬號(hào)并發(fā)布私有包Verdaccio默認(rèn)支持npm adduser注冊(cè)npm adduser --registry http://服務(wù)器IP:4873按要求輸入用戶(hù)名、密碼、郵箱后本地會(huì)寫(xiě)入認(rèn)證token。之后發(fā)布包之前先確認(rèn)package.json里的name是不是帶scope的{ name: team/component, version: 1.0.0, publishConfig: { registry: http://服務(wù)器IP:4873 } }然后執(zhí)行npm publish發(fā)布成功后如果配置了proxy其他人直接npm install team/component就能從本地倉(cāng)庫(kù)拉到。因?yàn)閜ublishConfig.registry已經(jīng)寫(xiě)死發(fā)布時(shí)不需要額外加--registry參數(shù)省得有人發(fā)到公網(wǎng)去。私有包版本管理跟公網(wǎng)包的語(yǔ)義化版本規(guī)則完全一樣新版本號(hào)必須大于已發(fā)布版本重復(fù)版本會(huì)報(bào)409。發(fā)錯(cuò)版本不要?jiǎng)h除整個(gè)包來(lái)補(bǔ)一個(gè)是Verdaccio的刪除在默認(rèn)配置下不完整另一個(gè)是別人可能已經(jīng)裝了舊版本刪除會(huì)導(dǎo)致鎖文件解析不了。正確做法是發(fā)一個(gè)新的patch版本并在發(fā)布說(shuō)明里標(biāo)注廢棄原因。還有一個(gè)建議私有包盡量加private: true或者至少把publishConfig的registry寫(xiě)死。不然哪天有人把某個(gè)內(nèi)部包的項(xiàng)目代碼拖出去直接執(zhí)行npm publish因?yàn)槿謗egistry還是官方源包就可能被誤發(fā)到公網(wǎng)。這種事故一旦發(fā)生相當(dāng)于公司內(nèi)部代碼泄露沒(méi)有任何后悔藥。我在團(tuán)隊(duì)里是把.npmrc和publishConfig都寫(xiě)進(jìn)腳手架模板強(qiáng)制帶上的。5.3 常見(jiàn)報(bào)錯(cuò)與解決思路下面是我在實(shí)際使用中高頻遇到的一些報(bào)錯(cuò)放在表格里方便對(duì)照定位報(bào)錯(cuò)關(guān)鍵詞可能原因處理方式403 Forbidden沒(méi)有發(fā)布權(quán)限或者用戶(hù)未登錄先npm adduser登錄再檢查Verdaccio配置里packages的publish權(quán)限409 Conflict包版本號(hào)已存在修改package.json版本號(hào)后重新發(fā)布ECONNREFUSED服務(wù)沒(méi)啟動(dòng)或者端口/地址寫(xiě)錯(cuò)用netstat -ano | findstr :4873檢查服務(wù)再核對(duì)registry地址ETARGET本地倉(cāng)庫(kù)和上游源都找不到該版本檢查uplinks和proxy配置確認(rèn)上游可以訪(fǎng)問(wèn)或確認(rèn)包名、版本拼寫(xiě)CERT_HAS_EXPIRED自簽名證書(shū)或者系統(tǒng)時(shí)間問(wèn)題確認(rèn)Verdaccio是否啟用了HTTPS是否使用了受信任證書(shū)ERESOLVE overriding peer dependency依賴(lài)樹(shù)中的peer沖突多數(shù)是生態(tài)包自身的問(wèn)題可臨時(shí)用--legacy-peer-deps安裝同時(shí)推動(dòng)上游修復(fù)npm warn deprecated node-domexception1.0.0依賴(lài)鏈上的過(guò)時(shí)包警告不影響功能留意后續(xù)升級(jí)即可這里單獨(dú)說(shuō)一下npm warn deprecated這一類(lèi)信息在日志里看著嚇人實(shí)際上只是npm在提示某個(gè)間接依賴(lài)已不再維護(hù)。不需要專(zhuān)門(mén)處理。真正需要關(guān)注的是ECONNREFUSED和權(quán)限類(lèi)的錯(cuò)誤這類(lèi)通常會(huì)直接導(dǎo)致安裝失敗。排查的時(shí)候我習(xí)慣先在服務(wù)器本機(jī)執(zhí)行一次安裝驗(yàn)證npm install some-package --registry http://localhost:4873如果本機(jī)能裝通說(shuō)明問(wèn)題出在客戶(hù)端到服務(wù)器的網(wǎng)絡(luò)路徑比如防火墻、端口封禁或者服務(wù)器監(jiān)聽(tīng)地址沒(méi)放開(kāi)。如果本機(jī)也裝不通再回頭檢查Verdaccio日志看是配置問(wèn)題還是上游回源的問(wèn)題。這個(gè)二分法在Windows環(huán)境里很管用能快速把問(wèn)題定位到網(wǎng)絡(luò)層還是服務(wù)層。6. 一點(diǎn)個(gè)人體會(huì)最后講一段自己的實(shí)際經(jīng)驗(yàn)。搭建這套Verdaccio PM2方案本身不難難的是把Windows環(huán)境里那些零碎問(wèn)題一次性理順尤其是PowerShell執(zhí)行策略、PATH環(huán)境變量和Windows服務(wù)自啟這三個(gè)點(diǎn)。我建議先把Verdaccio用前臺(tái)模式跑通確認(rèn)包能發(fā)、能裝再交給PM2托管最后再做開(kāi)機(jī)自啟和防火墻放行。順序反了的話(huà)出了問(wèn)題就分不清是配置問(wèn)題還是服務(wù)問(wèn)題。倉(cāng)庫(kù)搭好之后一定要記得定時(shí)備份storage目錄和htpasswd文件這倆一個(gè)是包數(shù)據(jù)一個(gè)是賬號(hào)數(shù)據(jù)丟了都挺折騰。我在團(tuán)隊(duì)里還專(zhuān)門(mén)把這兩個(gè)目錄的備份寫(xiě)進(jìn)了巡檢腳本每周自動(dòng)打包一次。如果你的團(tuán)隊(duì)準(zhǔn)備長(zhǎng)期使用這套方案這個(gè)習(xí)慣越早建立越好。還有一個(gè)小技巧升級(jí)Verdaccio之前把現(xiàn)有storage目錄復(fù)制一份再執(zhí)行npm更新一旦新版本配置格式有變化可以快速回滾。升級(jí)這種事最怕的就是服務(wù)起不來(lái)了數(shù)據(jù)還在復(fù)制一份無(wú)傷大雅關(guān)鍵時(shí)刻能救命。