
搞前端或者說(shuō) Node 生態(tài)的同學(xué)幾乎都經(jīng)歷過(guò)這樣的事接口行為突然變了查了半天發(fā)現(xiàn)是依賴被悄悄升級(jí)了安全公告要求盡快升到修復(fù)版本你卻連這個(gè)包到底有哪些可用版本都沒(méi)概念同事拉下來(lái)一個(gè)倉(cāng)庫(kù)package.json里寫(xiě)著^2.3.4本地跑得好好的一到 CI 上卻裝出了另一個(gè)版本。這種時(shí)候大家的第一反應(yīng)往往是同一個(gè)動(dòng)作——先查一下這個(gè)包的版本信息??蒼pm查看包版本這件事表面上是一條npm view命令的事實(shí)際用起來(lái)卻包含好幾個(gè)容易混淆的輸出字段、好幾層不同的版本概念如果不把它們理清楚很容易拿到錯(cuò)誤信息做出錯(cuò)誤判斷。這篇就專門(mén)聊聊怎么高效、準(zhǔn)確地查看一個(gè) npm 包的版本信息從遠(yuǎn)端倉(cāng)庫(kù)到本地node_modules從命令行到鎖文件把常用的方法和踩過(guò)的坑一次講透。1. 版本不匹配引發(fā)的三道難題為什么查版本不是看一眼這么簡(jiǎn)單很多初學(xué)者會(huì)問(wèn)查看包版本不就是npm view xxx version嗎沒(méi)錯(cuò)這條命令確實(shí)是核心工具但實(shí)際工作中遇到的版本混亂遠(yuǎn)比單純查詢要復(fù)雜。我見(jiàn)過(guò)太多因?yàn)榘姹拘畔](méi)查清楚導(dǎo)致的故障這里先說(shuō)三個(gè)典型場(chǎng)景。1.1 新版本發(fā)布后安全升級(jí)怎么評(píng)估某個(gè)依賴包發(fā)了新版本修復(fù)了一個(gè)安全漏洞公告里寫(xiě)著請(qǐng)升級(jí)到 3.2.5 及以上版本。這時(shí)候你抬手指敲npm view xxx version返回3.2.5。看起來(lái)沒(méi)問(wèn)題但你有沒(méi)有想過(guò)這個(gè)包的最新版確實(shí)是 3.2.5可你的項(xiàng)目還停留在 2.x直接升到 3.x 可能伴隨 breaking change或者這個(gè)包另外維護(hù)了一個(gè) long-term 版本線最新版是 3.x但 2.x 線也有安全性修復(fù)版本 2.9.8只不過(guò)它沒(méi)有被標(biāo)記為latest。只看version字段是遠(yuǎn)遠(yuǎn)不夠的。你需要把它的完整版本列表拉出來(lái)再對(duì)比dist-tags搞清楚維護(hù)者把哪個(gè)版本標(biāo)記為latest、哪個(gè)標(biāo)記為next、哪個(gè)標(biāo)記為beta。這是一道信息收集題不是一條命令能草草回答的。1.2 聲明版本和實(shí)際安裝版本對(duì)不上問(wèn)題出在哪你打開(kāi)package.json看到某個(gè)依賴寫(xiě)的是^4.1.0于是想當(dāng)然地認(rèn)為項(xiàng)目里跑的就是 4.1.0。但node_modules里實(shí)際安裝的可能是 4.5.3因?yàn)閊允許次版本號(hào)浮動(dòng)只要主版本號(hào)一致npm 就會(huì)在安裝時(shí)選取滿足范圍的最新版。等到package-lock.json缺失、或者是別人用不同版本 npm 重新生成過(guò)鎖文件實(shí)際安裝結(jié)果可能跟你本地完全不同。這種聲明版本和實(shí)裝版本的差異是很多詭異 bug 的根源。要定位問(wèn)題你需要的不是看package.json而是查node_modules里的真實(shí)版本、查鎖文件里凍結(jié)的版本、查遠(yuǎn)端當(dāng)前滿足范圍的最新版本三個(gè)數(shù)據(jù)對(duì)齊之后才能下結(jié)論。1.3 多個(gè)約束同時(shí)命中npm 到底選了哪個(gè)稍微復(fù)雜一點(diǎn)的項(xiàng)目里同一個(gè)包可能同時(shí)出現(xiàn)在多級(jí)依賴關(guān)系中。比如你的直接依賴要求some-lib^2.0.0另一個(gè)工具包的間接依賴要求some-lib~2.2.0。此時(shí) npm 會(huì)嘗試解析出一個(gè)同時(shí)滿足所有范圍的版本。但如果你對(duì) npm 的解析機(jī)制不熟靠肉眼根本推算不出來(lái)。這種情況下用npm view查看版本列表只能告訴你有哪些版本可以用真正決定裝了哪個(gè)的是npm ls和package-lock.json里的記錄。這三個(gè)場(chǎng)景共同說(shuō)明一件事查版本信息不是一個(gè)孤立動(dòng)作而是一套可以拆解的技能。遠(yuǎn)端的版本信息、本地的實(shí)際版本、鎖文件里的凍結(jié)版本各有各的查法也各有各的坑。2. npm view 命令全拆解從默認(rèn)輸出到精準(zhǔn)字段過(guò)濾npm view別名npm info、npm show是查看 npm 包元數(shù)據(jù)的主要入口。很多人只用過(guò)默認(rèn)輸出實(shí)際上它的字段過(guò)濾能力非常強(qiáng)。2.1 不帶參數(shù)時(shí)的默認(rèn)輸出到底輸出了什么最簡(jiǎn)單的用法是直接執(zhí)行npm view some-pkg這里的some-pkg替換成你要查的真實(shí)包名。默認(rèn)情況下npm 會(huì)把從 registry 拉到的元數(shù)據(jù)里比較關(guān)鍵的信息打印出來(lái)包括名稱、描述、當(dāng)前dist-tags、版本列表的摘要、維護(hù)者、倉(cāng)庫(kù)地址、最新版本的依賴等等。輸出比較長(zhǎng)但全是核心字段。值得說(shuō)明的是npm view不帶參數(shù)時(shí)大部分字段取的其實(shí)是dist-tags.latest對(duì)應(yīng)的那個(gè)版本的元數(shù)據(jù)而不是整個(gè)包的所有信息。也就是說(shuō)它展示的是這個(gè)包最新穩(wěn)定版長(zhǎng)什么樣。如果你發(fā)現(xiàn)輸出里的version是 5.2.0這并不代表這個(gè)包只有 5.2.0只是說(shuō) 5.2.0 被標(biāo)記為latest。2.2 用 versions 拉出完整版本清單想看這個(gè)包歷史上發(fā)布過(guò)哪些版本用npm view some-pkg versions --json--json加上之后輸出就是一個(gè)標(biāo)準(zhǔn)的 JSON 數(shù)組比如[ 1.0.0, 1.0.1, 1.1.0, 2.0.0 ]這里要提個(gè)建議沒(méi)有特別強(qiáng)的理由不要直接執(zhí)行npm view some-pkg versions不帶--json。因?yàn)榘姹径嗟臅r(shí)候默認(rèn)格式會(huì)換成類似[ 1.0.0, ... ]的數(shù)組表示還能勉強(qiáng)看版本上百個(gè)時(shí)輸出會(huì)摻雜引號(hào)和逗號(hào)眼睛很容易看花。加--json然后復(fù)制到編輯器里做篩選是更舒服的做法。如果你想統(tǒng)計(jì)某個(gè)包一共發(fā)過(guò)多少個(gè)版本可以配合node或者jqnpm view some-pkg versions --json | jq length這個(gè)技巧在評(píng)估歷史版本規(guī)模時(shí)很實(shí)用。比如某個(gè)包五年發(fā)了三百多個(gè)版本說(shuō)明它迭代頻繁升級(jí)前更需要謹(jǐn)慎核對(duì) API 變化。2.3 version 與 dist-tags 的微妙區(qū)別npm view some-pkg version返回的是dist-tags.latest也就是當(dāng)前被標(biāo)記為最新穩(wěn)定版的版本號(hào)。注意這個(gè)編號(hào)不一定比所有歷史版本都大。有些項(xiàng)目會(huì)維護(hù)多個(gè)發(fā)布線比如同時(shí)維護(hù) 1.x 和 2.x最新修復(fù)版本可能是2.0.5但某個(gè)實(shí)驗(yàn)性的3.0.0-beta.1雖然版本號(hào)更大卻不會(huì)被latest指向。更完整的標(biāo)簽體系用npm view some-pkg dist-tags --json返回結(jié)果類似{ latest: 2.0.5, next: 3.0.0-beta.1, beta: 3.0.0-beta.2 }這個(gè)信息非常有價(jià)值。一看就知道這個(gè)包當(dāng)前穩(wěn)定版是 2.0.5同時(shí)還有個(gè)next標(biāo)簽指向 3.0.0 測(cè)試版說(shuō)明團(tuán)隊(duì)正在開(kāi)發(fā)下一個(gè)大版本。你在npm install some-pkg時(shí)默認(rèn)拿到的就是latest對(duì)應(yīng)的版本想嘗鮮就用npm install some-pkgnext。查版本信息時(shí)不看dist-tags等于只看了冰山一角。2.4 定點(diǎn)查看某個(gè)歷史版本有時(shí)候我們要確認(rèn)某個(gè)具體版本的信息比如它發(fā)布于何時(shí)、依賴了哪些包、Node 版本要求是多少。可以用npm view some-pkg1.2.3把版本號(hào)寫(xiě)在包名后面就能拿到指定版本的元數(shù)據(jù)而不是默認(rèn)的latest。這對(duì)于排查為什么升到某個(gè)版本后構(gòu)建失敗非常有用。比如你想確認(rèn) 1.2.3 版對(duì) Node 版本的要求可以執(zhí)行npm view some-pkg1.2.3 engines輸出可能是{ node: 12.0.0, npm: 6.0.0 }這能解釋很多奇怪的問(wèn)題為什么本地 Node 16 跑得歡同事的 Node 14 卻裝不上某個(gè)版本因?yàn)樵摪姹镜脑獢?shù)據(jù)里明確寫(xiě)了engines限制。3. 用 --json 和字段過(guò)濾把元數(shù)據(jù)當(dāng)成數(shù)據(jù)庫(kù)來(lái)查npm view最強(qiáng)大的地方在于它可以像數(shù)據(jù)庫(kù)查詢一樣只取你關(guān)心的字段。這比把整個(gè)元數(shù)據(jù)打印出來(lái)再人工翻找高效得多。3.1 --json 讓輸出變成結(jié)構(gòu)化數(shù)據(jù)不管查什么字段習(xí)慣性帶上--json都是個(gè)好習(xí)慣。舉個(gè)例子你想看這個(gè)包所有版本的發(fā)布時(shí)間可以用npm view some-pkg time --json輸出會(huì)是一個(gè)對(duì)象鍵是版本號(hào)值是對(duì)應(yīng)的發(fā)布時(shí)間{ created: 2019-05-10T08:32:10.000Z, 1.0.0: 2019-05-10T08:33:41.000Z, 1.0.1: 2019-06-01T12:00:00.000Z, modified: 2024-03-18T09:00:00.000Z }created和modified分別表示這個(gè)包第一次發(fā)布和最后一次修改的時(shí)間。這個(gè)字段對(duì)我判斷一個(gè)包是否還活躍維護(hù)特別有用——如果最后一次修改是好幾年前那就要警惕它可能存在長(zhǎng)期未修復(fù)的安全問(wèn)題。3.2 幾個(gè)高頻字段逐一說(shuō)清用途查版本信息時(shí)下面這些字段是我用得最多的字段命令示例返回內(nèi)容典型用途最新版本號(hào)npm view some-pkg version單個(gè)版本字符串快速確認(rèn)當(dāng)前穩(wěn)定版完整版本列表npm view some-pkg versions --json版本字符串?dāng)?shù)組評(píng)估歷史版本、找可用版本發(fā)布標(biāo)簽npm view some-pkg dist-tags --json標(biāo)簽對(duì)象區(qū)分 latest、next、beta發(fā)布時(shí)間線npm view some-pkg time --json版本與時(shí)間映射判斷包活躍度運(yùn)行環(huán)境要求npm view some-pkg enginesNode/npm 版本要求排查環(huán)境兼容性依賴聲明npm view some-pkg dependencies依賴對(duì)象評(píng)估升級(jí)影響面同版本同依賴測(cè)試npm view some-pkg2.0.0 dependencies指定版本依賴確認(rèn)特定版本的依賴樹(shù)這幾個(gè)字段基本覆蓋了日常 90% 的版本查詢需求。組合起來(lái)使用可以解決從這個(gè)包能不能升到升了會(huì)影響誰(shuí)的完整鏈路問(wèn)題。3.3 查詢沉沒(méi)了的老版本還有個(gè)容易被忽視的點(diǎn)npm view some-pkg0.9.0可以查到很老的版本信息但這不代表你應(yīng)該立刻在生產(chǎn)環(huán)境用它。老版本往往有比較嚴(yán)重的已知漏洞npm 官方對(duì)元數(shù)據(jù)里的deprecated字段會(huì)做標(biāo)注。查看方式npm view some-pkg0.9.0 deprecated如果返回了一段字符串說(shuō)明這個(gè)版本被維護(hù)者標(biāo)記為廢棄字符串內(nèi)容一般解釋了廢棄原因和替代方案。查版本的時(shí)候順手看一眼這個(gè)字段能避免安裝到明明存在但已不建議使用的版本。4. 本地已安裝包的真實(shí)版本node_modules 與 npm ls 的幾種視圖查遠(yuǎn)端版本只是第一步真正決定項(xiàng)目行為的是本地安裝的實(shí)際版本。很多人會(huì)把package.json里的聲明當(dāng)成實(shí)際運(yùn)行時(shí)版本這是最常見(jiàn)的誤區(qū)。4.1 npm ls 查詢依賴樹(shù)中的實(shí)際版本想看清楚某個(gè)包在當(dāng)前項(xiàng)目里實(shí)際裝的是哪個(gè)版本用npm ls some-pkgnpm 會(huì)根據(jù)node_modules的真實(shí)物理目錄向上回溯并生成依賴樹(shù)然后精確告訴你project-name1.0.0 └─┬ other-pkg3.4.1 └── some-pkg2.1.0這說(shuō)明some-pkg的實(shí)際安裝版本是 2.1.0而且它不是一個(gè)頂層依賴而是被other-pkg間接引入的。如果你直接看package.json里的dependencies可能完全找不到它的名字——這種情況在排查安全公告時(shí)非常常見(jiàn)。想要看全量依賴樹(shù)可以把包名去掉直接執(zhí)行npm ls --depth0只看頂層依賴或者npm ls --all看整棵依賴樹(shù)。不過(guò)--all輸出巨大我一般不會(huì)直接跑而是用npm ls some-pkg精確定位目標(biāo)包。4.2 全局安裝包的版本怎么查全局安裝的包不在當(dāng)前項(xiàng)目的node_modules里查詢方式稍有不同npm ls -g --depth0這會(huì)列出所有全局安裝的頂層包及其版本。如果想只看某一個(gè)npm ls -g some-pkg這條命令跟非全局用法幾乎一樣只是加上-g。如果你覺(jué)得輸出太亂還可以用npm list -g --depth0 --json拿到結(jié)構(gòu)化結(jié)果。需要注意的是全局包常常執(zhí)行時(shí)用的版本和你npm view看到的latest不一致——因?yàn)槿职粫?huì)自動(dòng)升級(jí)除非你手動(dòng)執(zhí)行npm install -g some-pkglatest。4.3 package-lock.json 是最終裁決記錄node_modules里的實(shí)際版本是安裝結(jié)果但如果你想知道當(dāng)前倉(cāng)庫(kù)承諾的是哪個(gè)版本直接看package-lock.json。這個(gè)文件里記錄了每次安裝時(shí)確定下來(lái)的精確版本號(hào)、依賴下載地址和完整性校驗(yàn)值。打開(kāi)鎖文件搜索對(duì)應(yīng)的包名你會(huì)看到類似這樣的結(jié)構(gòu){ node_modules/some-pkg: { version: 2.1.0, resolved: https://registry.example.com/some-pkg/-/some-pkg-2.1.0.tgz, integrity: sha512-... } }version字段是精確版本resolved是下載地址integrity是完整性校驗(yàn)值。當(dāng)你在 CI 上發(fā)現(xiàn)裝出的版本和本地不一致時(shí)第一件事就是比對(duì)兩邊package-lock.json里該包的version和resolved地址。如果不同幾乎可以斷定是兩端拉取了不同的鎖文件。4.4 查不出版本時(shí)的 ERESOLVE 問(wèn)題npm ls some-pkg有時(shí)會(huì)直接報(bào)ERESOLVE錯(cuò)誤而中斷顯示無(wú)法滿足依賴關(guān)系。這不是查不了版本而是依賴樹(shù)中存在沖突npm 用錯(cuò)誤提示替代了正常輸出。這恰恰暴露了版本信息之間的深層矛盾。這種時(shí)候不要慌先執(zhí)行npm ls some-pkg --all --json看看能否強(qiáng)行輸出部分信息。如果還是不行可以手動(dòng)查看鎖文件確認(rèn)根因往往是某個(gè)包的某個(gè)依賴同時(shí)被多個(gè)祖先要求了互不兼容的版本范圍。修復(fù)方案不是硬刪node_modules而是要么在頂層顯式安裝一個(gè)雙方范圍內(nèi)都能接受的版本要么調(diào)整依賴聲明讓它們達(dá)成共識(shí)。5. package.json 版本范圍引發(fā)的幽靈版本問(wèn)題很多時(shí)候你查到的版本信息和實(shí)際使用的對(duì)不上并不是命令用錯(cuò)了而是package.json里的版本范圍寫(xiě)法讓最終版本成了一個(gè)變動(dòng)的值。5.1 ^、~、* 這些符號(hào)到底代表什么package.json里的依賴版本通常不是精確值而是語(yǔ)義化版本范圍。最常見(jiàn)的幾個(gè)符號(hào)意思如下寫(xiě)法含義舉例1.2.3精確版本只裝這個(gè)版本1.2.3^1.2.3允許升級(jí)次版本和補(bǔ)丁版本不允許大版本變更1.2.3 2.0.0~1.2.3只允許補(bǔ)丁版本升級(jí)1.2.3 1.3.01.2.3 2.0.0自定義范圍范圍內(nèi)任意版本*任意版本任意^是最常見(jiàn)的也是幽靈版本的重要來(lái)源。它保證了向后兼容的預(yù)期但并不能保證每次安裝結(jié)果完全一致。假設(shè)你聲明的是^1.2.3今天裝可能得到 1.2.8下周維護(hù)者發(fā)布了 1.3.0 且它滿足^1.2.3那次重新安裝就會(huì)得到 1.3.0。如果 1.3.0 里有微小的行為變化你的項(xiàng)目就會(huì)莫名發(fā)生變化。5.2 范圍相同但 lockfile 不同裝出來(lái)的版本可能不同有人會(huì)說(shuō)不對(duì)啊我們項(xiàng)目明明有鎖文件怎么會(huì)變 關(guān)鍵就在這里。package-lock.json存在的情況下執(zhí)行npm install會(huì)優(yōu)先遵循鎖文件里凍結(jié)的精確版本此時(shí)范圍符號(hào)幾乎不影響結(jié)果。但是鎖文件也會(huì)過(guò)期或者被重新生成執(zhí)行npm install some-pkglatest時(shí)npm 會(huì)更新鎖文件里該包相關(guān)的版本信息刪除鎖文件后重新安裝npm 會(huì)按照package.json的范圍重新解析出當(dāng)時(shí)滿足條件的版本不同 npm 版本對(duì)同一范圍的處理方式可能存在細(xì)微差異。所以我的經(jīng)驗(yàn)是查版本信息時(shí)優(yōu)先從鎖文件讀精確版本然后從package.json讀范圍兩者不一致但鎖文件存在時(shí)以鎖文件為準(zhǔn)。沒(méi)有鎖文件的話理論上任何人執(zhí)行npm install拿到的版本都可能不同排查這類問(wèn)題的核心就是補(bǔ)全并提交鎖文件。5.3 緩存與 registry 導(dǎo)致版本信息失真除了范圍符號(hào)本地緩存和鏡像源也可能讓你看到假版本。npm view默認(rèn)走的是 npm 公共 registry但如果你配置了私有源或者鏡像看到的結(jié)果可能和官方源不一致。我遇到過(guò)一種情況公司內(nèi)部源沒(méi)有及時(shí)同步某個(gè)包的最新版本執(zhí)行npm view some-pkg version返回的還是老版本而同事在官方源環(huán)境里已經(jīng)看到新版本。查這個(gè)包的版本時(shí)最好先確認(rèn)當(dāng)前 registrynpm config get registry如果 registry 指向的不是預(yù)期地址可以在命令后面臨時(shí)用參數(shù)覆蓋npm view some-pkg version --registryhttps://registry.npmjs.org此外npm本身有本地緩存。雖然執(zhí)行npm view時(shí)通常會(huì)發(fā)起實(shí)時(shí)請(qǐng)求但緩存異常偶有發(fā)生導(dǎo)致拿到舊元數(shù)據(jù)。如果你懷疑緩存問(wèn)題優(yōu)先用npm cache verify檢查而不是動(dòng)不動(dòng)就npm cache clean --force。前者是安全驗(yàn)證緩存完整性后者是粗暴刪緩存副作用大且往往解決不了真正問(wèn)題。6. 實(shí)踐中的版本核查流程三步定位版本是否可用工具和方法都講完了最后分享一套我自己在項(xiàng)目排查中反復(fù)使用的版本核查流程按順序走下來(lái)大部分依賴版本問(wèn)題都能在十分鐘內(nèi)定位。6.1 第一步遠(yuǎn)端可用版本與本機(jī)實(shí)裝版本比對(duì)拿到一個(gè)奇怪版本號(hào)或者遇到異常行為時(shí)先并行查兩組數(shù)據(jù)遠(yuǎn)端可用信息npm view some-pkg versions --json、npm view some-pkg dist-tags --json本機(jī)實(shí)際信息npm ls some-pkg和package-lock.json中的鎖定信息把兩端對(duì)照通常馬上就能發(fā)現(xiàn)問(wèn)題是本地版本太舊、鎖文件被改過(guò)還是遠(yuǎn)端版本已經(jīng)更新但本地沒(méi)同步。我一般會(huì)先執(zhí)行npm ls some-pkg --depth0 2/dev/null || npm ls some-pkg來(lái)拿實(shí)際版本再執(zhí)行npm view some-pkg version拿最新穩(wěn)定版。兩者不一致且項(xiàng)目里存在異常行為時(shí)優(yōu)先懷疑行為差異來(lái)自版本差異。6.2 第二步用依賴記錄評(píng)估升級(jí)影響面如果確認(rèn)需要升級(jí)或降級(jí)不要直接改package.json然后npm install。先看看目標(biāo)版本的依賴關(guān)系npm view some-pkg目標(biāo)版本 dependencies --json npm view some-pkg目標(biāo)版本 engines把目標(biāo)版本的依賴和當(dāng)前使用版本的依賴做一次 diff重點(diǎn)關(guān)注是否有新增的peerDependencies要求以及 Node 版本要求是否提高。這些信息能提前預(yù)判升級(jí)后可能出現(xiàn)的兼容性問(wèn)題。順便看一眼deprecated字段如果維護(hù)者標(biāo)記了廢棄就要考慮更換替代包而不是繼續(xù)糾結(jié)版本。6.3 第三步安裝后驗(yàn)證版本而不是信任聲明操作完升級(jí)后一定要回到實(shí)際安裝版本去看而不是只有package.json里的聲明。執(zhí)行npm ls some-pkg node -e console.log(require(some-pkg/package.json).version)這兩條命令分別從依賴樹(shù)和包自身的package.json字段里讀取版本。第二條尤其適合用來(lái)判斷某個(gè)工具包是否真的是你期望的那個(gè)版本因?yàn)楹芏喟诎惭b階段可能會(huì)做版本替換或 patch 處理比如使用補(bǔ)丁工具這時(shí)候只有運(yùn)行時(shí)讀取到的版本才算數(shù)。驗(yàn)證通過(guò)后順手推送更新后的package-lock.json到倉(cāng)庫(kù)。鎖文件是版本信息的防篡改記錄只要鎖文件一致理論上團(tuán)隊(duì)成員和 CI 都會(huì)安裝相同的版本。沒(méi)有人希望我本地跟 CI 上裝的依賴版本不一樣成為一周后才發(fā)現(xiàn)的真相。我在實(shí)際操作中還有個(gè)習(xí)慣把帶--json的版本查詢結(jié)果歸一化統(tǒng)一用npm view some-pkg --json拿整個(gè)元數(shù)據(jù)存成一份備份后面無(wú)論排查什么問(wèn)題都可以在本地 JSON 文件里快速搜索字段而不是一遍遍敲命令請(qǐng)求遠(yuǎn)端。版本信息這東西看著瑣碎關(guān)鍵時(shí)刻能救場(chǎng)。你排查得越多就越會(huì)體會(huì)到真正可靠的永遠(yuǎn)不是你以為的版本而是你實(shí)際查到的版本。