爲何我從 npm 到 yarn 再到 npm?

first post on http://blog.xgheaven.com/2018/05/03/npm-to-yarn-to-npm/html

從接觸到 node 環境來講,其中一個不可或缺的一部分即是 npm 包管理,可是因爲官方的 npm 有各類各樣的問題,因而催生了不少不一樣的版本,這其中的曲折也許只有過來人才知道。node

放棄 npm?

上古時代

在上古版本(應該是 npm3 之前的版本,具體我也記不清了),npm 的安裝策略並非扁平化的,也就是說好比你安裝一個 express,那麼你會在 node_modules 下面只找到一個 express 的文件夾。而 express 依賴的項目都放在其文件夾下。linux

- app/
  - package.json
  - node_modules/
    - express/
      - index.js
      - package.json
      - node_modules/
        - ...
複製代碼

這個帶來的問題或許 windows 用戶深諳其痛,由於在這種安裝環境下,會致使目錄的層級特別高,而對於 windows 來講,最大的路徑長度限制在 248 個字符(更多請見此),再加上 node_modules 這個單詞又特別長,因此你懂得,哈哈哈。解決方案啥的本身去搜索吧,反正估計如今也沒人會用上古版本了。git

除了 windows 用戶出現的問題之外,還有一個更嚴重的問題,就是模塊都是獨立的,好比說位於 express 下面的 path-to-regexpconnect 下面的 path-to-regexp 的模塊是兩個不一樣的模塊。 那麼這個會帶來什麼影響呢?其實在使用上,並無什麼太大的影響,可是內存佔用過大。由於不少相同模塊位於不一樣模塊下面就會致使有多個實例的出現(爲何會加載多個實例,請查看 Node 模塊加載)。你想一想,都是一樣的功能,爲何要實例這麼屢次呢?不能就加載一次,複用實例麼?github

上古時代的 npm 的缺點能夠說仍是不少的:express

  • 目錄嵌套層級過深
  • 模塊實例沒法共享
  • 安裝速度很慢,這其中有目錄嵌套的緣由,也有安裝邏輯的問題。由於 npm 是請求完一個模塊以後再去請求另外一個模塊,這就會致使同一個時刻,只有一個模塊在下載、解析、安裝。

軟鏈時代

後面,有人爲了解決目錄嵌套層次太高的問題,引入的軟連接的方案。npm

簡單來講,就是將全部的包都扁平化安裝到一個位置,而後經過軟連接(windows 快捷方式)的方式組合到 node_modules 中。json

- app/
- node_modules
  - .modules/
    - express@x.x.x/
      - node_modules
        - connect -> ../../connect@x.x.x
        - path-to-regexp -> ../../path-to-regexp@x.x.x
        - ... -> ../../package-name@x.x.x
    - connect@x.x.x/
    - path-to-regexp@x.x.x/
    - ...others
  - express -> ./.modules/express@x.x.x
複製代碼

這樣作的好處就是能夠將總體的邏輯層級簡化到不多的幾層。並且對於 node 的模塊解析來講,能夠很好的解決相同模塊不一樣位置致使的加載多個實例,進而致使內存佔用的狀況。windows

基於這種方案,有 npminstall 以及 pnpm 這個包實現了這種方案,其中 cnpm 使用的就是 npminstall,不過他們實現的方式和我上面講的是有差別的,具體請看。簡單來說,他們沒有 .modules 這一層。更多的內容,請看 npminstall 的 README。緩存

總的來說這種解決方案有還有如下幾個好處:

  • 兼容性很好
  • 在保證目錄足夠簡潔的狀況下,解決了上面的兩個問題(目錄嵌套和多實例加載)。
  • 安裝速度很快,由於採用了軟鏈接的方式加上多線程請求,多個模塊同時下載、解析、安裝。

那麼缺點也是挺致命的:

  • 通常狀況下都是第三方庫實現這個功能,因此沒法保證和 npm 徹底一致的行爲,因此遇到問題只能去找做者提交一下,而後等待修復。
  • 沒法和 npm 很方便的一塊兒使用。最好是要麼只用 npm,要麼只用 cnpm/pnpm,二者混用可能會產生很奇葩的效果。

npm3 時代

最大的改變就是將目錄層級從嵌套變到扁平化,能夠說很好的解決了上面嵌套層級過深以及實例不共享的問題。可是,npm3 在扁平化方案下,選擇的並非軟鏈接的方式,而是說直接將全部模塊都安裝到 node_modules 下面。

- app/
- node_modules/
  - express/
  - connect/
  - path-to-regexp/
  - ...
複製代碼

若是出現了不一樣版本的依賴,好比說 package-a 依賴 package-c@0.x.x 的版本,而 package-b 依賴 package-c@1.x.x 版本,那麼解決方案仍是像以前的那種嵌套模式同樣。

- app/
- node_modules/
  - package-a/
  - package-c/
    - // 0.x.x
  - package-b/
    - node_modules/
      - package-c/
        - // 1.x.x
複製代碼

至於那個版本在外面,那個版本在裏面,彷佛是根據安裝的前後順序有關的,具體的我就不驗證了。若是有人知道的話,歡迎告訴我。

在這個版本以後,解決了大部分問題,能夠說 npm 跨入了一個新的世界。可是還要一個問題就是,他的安裝速度依舊很慢,相比 cnpm 來講。因此他還有不少進步的空間。

yarn 的誕生

隨着 Node 社區的愈來愈大,也有愈來愈多的人將 Node 應用到企業級項目。這也讓 npm 暴露出不少問題:

  • 沒法保證兩次安裝的版本是徹底相同的。你們都知道 npm 經過語義化的版本號安裝應用,你能夠限制你安裝模塊的版本號,可是你沒法限制你安裝模塊依賴的模塊的版本號。即便有 shrinkwrap 的存在,可是不多有人會用。
  • 安裝速度慢。上文已經講過,在一些大的項目當中,可能依賴了上千個包,甚至還包括了 C++ Addon,嚴重的話,安裝可能要耗時 10 分鐘甚至到達半個小時。這很明顯是沒法忍受的,尤爲是配合上 CI/CD。
  • 默認狀況下,npm 是不支持離線模式的,可是在有些狀況下,公司的網絡可能不支持鏈接外網,這個時候利用緩存構建應用就是很方便的一件事情。並且能夠大大減小網絡請求。

因此,此時 yarn 誕生了,爲的就是解決上面幾個問題。

  • 引入 yarn.lock 文件來管理依賴版本問題,保證每次安裝都是一致的。
  • 緩存加並行下載保證了安裝速度

那個時候我還在使用 cnpm,我特意比較了一下,發現仍是 cnpm 比較快,因而我仍是繼續使用着 cnpm,由於對於我來講足夠了。可是後面發現 yarn 真的愈來愈火,再加上 cnpm 長久不更新。我也嘗試着去了用 yarn,在嘗試以後,我完全放棄了 cnpm。並且直到如今,彷佛尚未加入 lock 的功能。

固然 yarn 還不僅只有這麼幾個好處,在用戶使用方面:

  • 提供了很是簡潔的命令,將相關的命令進行分組,好比說 yarn global 下面都是與全局模塊相關的命令。並且提示很是徹底,一眼就能看明白是什麼意思。不會像 npm 同樣,npm --help 就是一坨字符串,還不講解一下是什麼用處,看着頭疼。
  • 默認狀況安裝會保存到 dependencies,不須要像 npm 同樣手動添加 -S 參數
  • 很是方便的 yarn run 命令,不只僅會自動查看 package.json 中 scripts 下面的內容,仍是查找 node_modules/.bin 下的可執行文件。這個是我用 yarn 最高的頻率。好比你安裝了 yarn add mocha,而後就能夠經過 yarn run mocha 直接運行 mocha。而不須要 ./node_modules/.bin/mocha 運行。是我最喜歡的一個功能
  • 交互式的版本依賴更新。npm 你只能先經過 npm outdated 看看那些包須要更新,而後經過 npm update [packages] 更新指定的包。而在 yarn 當中,能夠經過交互式的方式,來選擇那些須要更新,那些不須要。
  • 全局模塊的管理。npm 管理全局模塊的方式是經過直接在 /usr/lib/node_modules 下面安裝,而後經過軟鏈接鏈接到 /usr/local/bin 目錄下。而 yarn 的作法是選擇一個目錄,這個目錄就是全局模塊安裝的地方,而後將全部的全局模塊當作一個項目,從而進行管理。這個好處就是,你能夠直接備份這個目錄當中的 package.json 和 yarn.lock 文件,從而能夠很方便的在另外一個地方還原你安裝了那些全局模塊。至於這個目錄的問題,經過 yarn global dir 命令就能夠找到,mac 下是在 ~/.config/yarn/global/,linux 我沒有測試過。

能夠說 yarn 用起來很是舒服,可是惟一的缺點就是否是 npm 官方出的,更新力度、兼容性都會差一些。但這也阻擋不住 yarn 在 Node 社區的火熱程度。很快,你們紛紛從 npm 切換到 yarn 上面。

重拾 npm 5

在受到 yarn 的衝擊以後,npm 官方也決定改進這幾個缺點,因而發佈了和 Yarn 對抗(這個詞是我意淫的)的 npm5 版本。

  1. 引入了 package-lock.json,而且默認就會添加,和 yarn.lock 是同樣的做用,而且取代以前的 npm shrinkwrap。
  2. 默認狀況下,安裝會自動添加 dependencies,不須要手動書寫 -S 參數
  3. 提高了安裝速度,和以前有了很大的進步,可是和 yarn 相比,仍是略微慢一些

至此,yarn 和 npm 的差距已經很是很是小了,更多的差距體如今用戶體驗層面,我使用 yarn 的功能也只剩下全局模塊管理、模塊交互式更新和 yarn run 這個命令了。

可是後面推出的 npx 讓我放棄了使用 yarn run 這個命令。不是說 npx 比 yarn 有多好,而是說 npm 集成了這個功能,也就不必再去使用第三方的工具了。並且 npx 還支持臨時安裝模塊,也就是那種只用一次的命令,用完就刪掉了。

後面我又發現了 npm-check 這個工具,我用它來替代了 yarn 的交互式更新。

然而 npm6 的出現加入了緩存,而且又進一步提高了速度,能夠說直逼 yarn。

因而 yarn 對我來講只剩下一個全局模塊管理的功能了。個人整個開發流程以及從 yarn 切換回 npm 上面了。或許後面的日子我也會讓 npm 來接管全局模塊管理,從而放棄使用 yarn。可是我仍是會裝 yarn,畢竟有一些老項目仍是用 yarn 的。

總結

我經歷了從 npm -> cnpm -> yarn -> (npm + npm-check + npx) 的一個循環,也見證了 npm 社區的一步步發展。並且 yarn 的更新頻率也很是慢,可能一個月才更新一次,這也讓我逐漸放棄使用 yarn。

有的時候感受,第三方的終究是第三方,仍是沒有原生的好用和方便,並且用起來安心。

相關文章
相關標籤/搜索