前言
從 Yarn 橫空出世推出 lock 檔案以來,已經兩年多時間了,npm 也在 5.0 版本加入了類似的功能,lock 檔案越來越被開發者們接收和認可。本篇文章想從前端視角探討一下我們為什麼需要 lock 檔案,以及它的一些成本與風險,當然其中一些觀點對於後端也是適用的。
為什麼需要 lock 檔案
之所以需要 lock 檔案,我覺得主要有 4 個原因:
確保各環境依賴版本的一致性
軟體開發一般有著好幾個環境,通常包括本地開發環境、整合測試環境、預釋出環境以及線上環境。各環境依賴版本不一致通常是 bug 的一大來源,大家可能碰到過“在我的電腦上是好的”這種問題,也許就是依賴版本不一致導致的。這種問題通常很難定位,因為你很難確定到底是自己的問題還是依賴的問題。這也是 Yarn 推出 lock 檔案的初衷之一,使用了 lock 檔案,你在排查問題時至少可以排除依賴版本不一致這個因素。
語義化版本並不絕對可靠
一些開發者不願意使用 lock 檔案,一個主要原因是他們希望基於語義化版本號讓依賴自動升級,認為只要選擇的依賴可靠,大版本不變化就可以放心升級。在絕大多數情況下這麼做不會有問題,但是也有意外情況,比如:
React 在 v16.4.0 對 getDerivedStateFromProps 的呼叫時機進行了調整。React 在 v16.3.0 引入了這個 API,最初只有在父元件引起的重渲染過程中會被呼叫,v16.4.0 調整為在所有渲染過程中都會被呼叫。如果你在不知情的情況下(自動)把 React 從 v16.3.0 升級到了 v16.4.0,那麼極端情況下你的應用就會出問題。
雖然只有在很極端的情況下你才會碰到類似問題,但是這種問題本來就是不怕一萬就怕萬一。
可控的升級依賴
現在通過 webpack 把依賴單獨提取為一個 vendor.js 是個很常見的做法,因為依賴變更相對來說沒那麼頻繁,再配合上強快取,可以做到即使釋出了新版本,使用者也可以使用快取的 vendor.js 而不必重新下載。通常我們的應用不止一個依賴,這些依賴肯定也不是同一時間釋出更新,如果不使用 lock 檔案讓其自由更新,可能會導致 vendor.js 快取失效多次(每個依賴更新都會導致快取失效)。如果使用 lock 檔案就可以積累一段時間,讓多個依賴集中更新,甚至跳過一些小版本更新,從而提高 vendor.js 的快取命中率。
安全問題
幾個月前 ESLint 發生了一個安全事故,一個攻擊者竊取了 ESLint 維護者的 npm 賬戶,併發布了惡意版本的 eslint-scope 和 eslint-config-eslint(都是更新的小版本),其中前者是 babel-eslint 和 webpack 的依賴。如果沒有使用 lock 檔案,那麼你就極有可能中招,ESLint 事後也建議開發者使用 lock 檔案來避免自動安裝新版本。
成本
使用 lock 檔案自然會增加一點專案的維護成本,因為依賴不會再自動升級,所以需要專案維護者每隔一段時間手動進行升級。另外如果兩個人同時修改了依賴,解決 lock 檔案的衝突也是一件很麻煩的事。
但是手動升級依賴也有一些額外的好處,至少你升級每個依賴時都要去看一下它的 change log,這樣可以對每一次升級做到心中有數,這也有助於你掌握依賴的發展趨勢。比如前文提到的 React 的例子,只要你在升級時看一眼它的 change log,就很容易避開可能出現的問題。
風險
我唯一能想到的風險就是依賴版本固化問題,如果你使用了 lock 檔案又沒有花時間跟精力去維護它,那麼你的專案就很容易陷入依賴版本固化的問題。如果太久沒有升級依賴,你當前使用的版本跟最新版差別太大,升級就會很困難,考慮到現實成本問題,可能就永遠不會升級了。
但是如果不使用 lock 檔案就能完全避免這個問題嗎,我想也不一定。不使用 lock 檔案最多也只能在同一個大版本範圍內自動升級,如果依賴升級了大版本,你沒有花時間去升級,也會碰到同樣的問題。只是相對於不使用 lock 檔案,問題暴露的晚一些而已。