因為使用PeerDependencies而引發的bug
前言
前幾天有個人跟我反饋說,她fork了我右鍵菜單那個開源項目,一直無法打包成功。我尋思著應該不可能吧,當我嘗試打包時,果然翻車了??。
經過了一番調試后,終于找到了問題所在,本文就跟大家分享下這個問題從發現到解決的整個過程,歡迎各位感興趣的開發者閱讀本文。
排查問題
因為我電腦重裝過幾次系統,一些放在github上的項目我就沒有備份,我把項目(https://github.com/likaia/vue-right-click-menu-next/)重新clone到本地,安裝依賴項后運行了build命令,意想不到的事情發生了:它報錯了??
- ERROR Failed to compile with 4 errors 11:02:26 AM
- error in ./src/components/right-menu.vue
- Module parse failed: Unexpected token (1:0)
- File was processed with these loaders:
- * ./node_modules/eslint-loader/index.js
- You may need an additional loader to handle the result of these loaders.
上述報錯的意思是找不到處理vue文件的相關loader,我就納悶了,這不可能啊,幾個月前插件寫好時還能打包的,現在咋就突然不能打包了呢。
可能是node版本的問題
難道是我node版本的問題?插件寫好到現在代碼一直沒動過,唯一變化的就是我升級了node版本,降級node版本太麻煩,于是我安裝了node版本管理工具n。
因為我的系統是macos,我可以直接用brew來安裝它,命令如下:
- brew install n
如果你是windows系統,你可以通過npm包的形式來安裝它,命令如下:
- npm install -g n
安裝完成后,我去找了下我寫這個項目時所發布的node版本v14.14.0,我們用n工具來安裝并切換它:
- n 14.14.0
我們運行node --version命令看下是否成功。
一切準備就緒,我尋思著應該不會出現問題了吧??,結果運行后,我傻眼了,仍然報著同樣的錯誤??
node版本管理工具有挺多的,除了文中說的n還有nvm、npx,感興趣的開發者可自行了解。
發現貓膩(yarn.lock)
當我一籌莫展發呆時,突然發現目錄樹中的yarn.lock變色了,看來是有改動了,我尋思著不可能啊,我沒動package.json中的依賴項啊,怎么會發生變化呢?
重新創建個項目試試
既然lock文件發生了變化,那我重新創建個項目試試,把相關依賴項拷過去再打包看看。
我們繼續使用Vue CLI作為插件搭建環境,對此不熟悉的開發者請移步我的另一篇文章:使用CLI開發一個Vue3的npm庫
- vue create test-vue3-project
項目創建完成后,我把相關文件拷貝了過去,修改了package.json中的build命令。
- {
- "build": "vue-cli-service build --target lib --name vueRightMenuPlugin src/main.ts"
- }
運行命令后,它居然打包成功了??
找到問題
經過前面的一番折騰,創建了一個新的項目他就好了,那我比對下這倆項目有啥不同之處,那么問題就迎刃而解了。
經過比對后,我發現了package.json中的不同之處:
- "dependencies": {
- "core-js": "^3.6.5",
- "vue": "^3.0.0"
- }
- "peerDependencies": {
- "core-js": "^3.6.5",
- "vue": "^3.0.0"
- }
區別就在于,vue和core-js這兩個包的位置,問題應該就出在這里了。
我們來驗證下吧,將dependencies中的那兩個包放到peerDependencies中,重新install下,再build看下。
不出意料,果然報錯了。
那么為啥我的項目之前能跑,現在卻沒法跑了,我想應該是因為之前改了后,我沒有重新install的緣故吧??。
解決問題
那么,既然找到問題了,我們反過來,把右鍵菜單的peerDependencies下的兩個包放到dependencies下,再看看問題能否得到解決。
當我滿懷信心的執行build命令后,結局卻讓我很失望。
是的,他換了個錯誤??
image-20210912132222990
看報錯是類型無法自動推導,這就很怪異了。那么就只能嘗試下我的三板斧了:
- 重啟軟件
- 重啟電腦
- 刪除項目,重新clone,重新install依賴
前兩個嘗試過后,發現并無卵用,只好用了最后一個方法。
重新install后,執行了build命令,成功解決了這個問題。
為什么呢
問題是解決了,那么為什么要那樣做呢?接下來就帶大家深入研究下dependencies和peerDependencies。
dependencies
dependencies是package.json中的一個屬性,里面放運行代碼時所需的依賴,在install時這些包會被安裝,打包項目時,這里面的包也會被打包進去。
peerDependencies
peerDependencies也是package.json中的一個屬性,這個單詞翻譯過來是對等依賴的意思,這里面的包在install時并不會安裝,打包項目時,這里面的包也不會被打包進去。
兩者存在的問題
如果將依賴包放在dependencies下,那么當別人在他的項目中引入你的插件時,會出現下述情況:
- 他項目里沒有引入你所需的依賴包,那么你插件所依賴的包會被安裝
- 他項目里引入了你所需的依賴包:
- 版本號一致,那么你所需的依賴包不會被安裝,插件將共用項目里的依賴包
- 版本號不一致,那么你所需的依賴包就會被安裝,項目里就存在了兩套不同版本的依賴
版本號一致那還好,萬事大吉。版本號不一致時,你插件所依賴的那個包需要的功能與調用者項目里安裝的那個版本的包并無區別,那么調用者的項目將變得臃腫起來,又多安裝了一份依賴。
如果將依賴包放在peerDependencies下,對插件開發者是不友好的,會出現下述問題:
- install的時候,所需的依賴不會安裝,使用ide開發時會報錯找不到相關依賴。
- build的時候,因為依賴未安裝,導致無法打包(文章開頭提到的報錯)
這么看的話,peerDependencies這個屬性,好像沒啥用了。當然存在即合理,如果大家有什么更好的看法,歡迎在評論區留言討論。
解決方案
知道他們各自的優點和缺點后,我也就知道了如何解決這個問題。
既然dependencies中的依賴包只要和調用者的版本號一致,就不需要重新安裝依賴,那我們把它的版本號放開,給個范圍,這樣不就可以了??
在package.json中的版本號可以帶下述符號:
- ~波浪號,匹配最新補丁版本號,即版本號的第三個數字,例如~3.0.0就會匹配3.0.x版本,將在3.1.0停止
- ^插入符號,匹配次要的版本號,即版本號的第二個數字,例如^3.0.0就會匹配任何3.x.x版本,將在4.0.0停止
- >、<、>=、<=比較運算符,匹配的就是這個區間的版本,例如>3.0.0 <= 3.1.4,就會匹配這個區間的版本號
如果不帶符號,那么它就是精確匹配。
本文中,用的是^3.0.0,滿足了我們插件的使用場景,因此不需要更改。