匯總貼:深入淺出管理 uni-app 依賴的 uvm
經驗分享的目標#
逛論壇發現有一些問題歸屬於@dcloudio/uvm
,問題相似,解答也相似,這裡做個匯總帖子,爭取能一次性解決常見問題。
本文目標是帶讀 uvm 源碼,熟悉和理解 uvm 設計思路和作用,並匯總常見問題,最終達到熟悉 uvm 源碼能自主解決問題的目的。
uvm 全稱是 uni-app version manager
,可以類比 nvm - node version manager
。
背景介紹#
uvm 作為官方的解決方案,在官網文檔 https://uniapp.dcloud.net.cn/quickstart-cli.html#cliversion 有明顯提及:
可以使用 @dcloudio/uvm 管理編譯器的版本,此工具僅自動增加或更新 uni-app 編譯器主要依賴,對於新增的編譯命令(scripts)暫時不會自動處理,需手動參考新工程進行配置。
內部跳轉 [[uni-app 學習 #uni-app 依賴升級 uvm]]
作為一個高速迭代的技術框架,uni-app 和周邊生態每天都會開發編碼、修復問題。出了常規的前端方案,還需要同時支持 hbuilderX,陳舊版本的依賴是沒有意義的,是有最新版本對當前項目有明顯的意義。
通過一種方案,實現前端依賴自動升級就顯得有必要了,這個過程最好是一鍵升級,不需要關注當前的版本,和最新的版本具體是什麼。
因此 @dcloudio/uvm
就出現了,在項目目錄中執行 npx @dcloudio/uvm
就能實現 package.json 相關依賴的自動更新。
出於一些歷史原因,uvm 的項目源碼沒有放到公網展示,但 npm 的產物沒有走壓縮編譯,閱讀產物源碼和開發源碼效果一致,這裡簡單介紹下源碼和大致代碼結構。
項目源碼#
訪問 npm 倉庫 @dcloudio/uvm,點擊 Code 部分。可以看到最終的產物,和原始代碼沒什麼區別。https://www.npmjs.com/package/@dcloudio/uvm?activeTab=code
首先看 package.json
識別相關依賴和註冊命令。
package.json#
"dependencies": {
"cross-spawn": "^7.0.3",
"inquirer": "^8.2.0",
"minimist": "^1.2.5",
"node-fetch": "^2"
},
"devDependencies": {
"xmldom": "^0.6.0"
}
依賴比較簡單,跨平台執行命令,交互式 cli,請求發送。相關的,主要還是看 bin
。
命令註冊看 bin 註冊 bin/index.js
,源碼如下,相對比較簡單和整齊。
通過閱讀源碼可以看到,當通過註冊命令啟動時候,會讀取當前 argv 參數,比如不傳的話默認填寫 latest
,也會讀取當前命令行所代表的項目地址。
流程#
在截圖 line30 會執行 start()
,會依次經歷 info()
find()
和 get()
,最後是 merge()
。
info()#
在 info()
方法中,會讀取當前目錄的 package.json
去分析 devDependencies
依賴是否包含下面的依賴:
const plugins = {
vue2: '@dcloudio/vue-cli-plugin-uni',
vue3: '@dcloudio/vite-plugin-uni'
}
最終返回對應的依賴和是否 vue3,這意味著 vue2 和 vue3 要升級的依賴是不同的。這裡挖個坑,後面帶讀 @dcloudio/vite-plugin-uni
依賴的原理,感興趣請評論區告訴我。
find()#
定位到 /@dcloudio/uvm/lib/version.js#find
。
在 find
方法中,會讀取當前的 plugin 、和 具體版本特徵值或者 "latest" 、是否 vue3 進行傳遞判斷。
實際處理使用拿到當前的版本最後一段數字,去遠程拉數據,不外乎 latest alpha release
三個取值。獲取遠程的 version 值。比如 3.96.2023110403
,這裡截圖打個斷點看看:
斷點進相關歷史判斷。
之後繼續請求 'https://registry.npmmirror.com/@dcloudio/vite-plugin-uni'
,還是各種特徵值判斷。
這裡會請求遠程數據,獲取 hbuilderX 版本信息,和 registry 信息。這裡是對網絡有要求。
這裡的 registry 默認是 cnpm ,這依賴 cnpm 的穩定性,有一定網絡波動的風險,但起碼能保證國內用戶能訪問通。
get()#
還會繼續判斷 get
方法,這裡以 vue3 的為例,最終回去獲取 https://gitee.com/dcloud/uni-preset-vue/blob/vite/package.json 的信息,也就是以遠程庫為基準進行處理。
為了兼容歷史版本的變更,這裡邏輯基本不可讀了,做了大量版本的兼容。在翻閱 ask 社區 uvm 問題時候,可以看到一些兼容的血淚史。
這裡如果是 vue3 會進入 getVue3()
,這裡默認會讀取 gitee 的遠程倉庫信息。如果出於某種原因,github 和 gitee 倉庫沒有正確同步,就會導致信息不匹配。
merge()#
這一步驟是最後的執行階段。會拿到 deps ,準備具體執行。使用 spawnAsync
去分別更新依賴和開發者依賴。
這一塊使用 cross-spawn
繞過不同系統執行命令的問題。
技術總結#
通過上面簡單的核心邏輯帶讀,可以看到 uvm
執行的本質思路是跨系統執行命令,獲取當前版本信息和遠程 gitee 上典型倉庫的 package.json,進行 複雜 判斷,得出要升級的結果。
常見 QA#
運行報錯提示 Not find version xxx ,請提供具體版本號
這個目前已經沒有類似問題了,對一些歷史陳舊的版本可能存在一定問題,如果你遇到了請提供你當前的版本號。
運行報錯提示
code ETARGET
可能是緩存和網絡問題,嘗試刪除緩存和當前 node_modules
,嘗試重新安裝依賴。
也可能是網絡同步問題,發版之後同步到國內需要一定時間,可以休息一下等一等再嘗試。