<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          npm & yarn 包管理機制

          共 9601字,需瀏覽 20分鐘

           ·

          2021-09-05 16:38

           大廠技術(shù)  堅持周更  精選好文

          背景

          使用 npm 或 yarn 管理項目依賴時,可能會產(chǎn)生以下疑問:

          1. 項目依賴出現(xiàn)問題怎么辦?刪了重裝,即先刪除 node_modules 再重新 install,那這樣的操作會不會存在風險?
          2. 把所有依賴都安裝到 dependencies 中,不區(qū)分 devDependencies 會有問題嗎?
          3. 我們的應(yīng)用依賴了 pkg-a 和 pkg-b,同時 pkg-a 也依賴了 pkg-b,那么 pkg-b 會被多次安裝或重復(fù)打包嗎?
          4. 一個項目中,我使用 npm 別人使用 yarn,這會引發(fā)什么問題?
          5. 我們是否要提交 lockfile(package-lock.json/yarn.lock) 到項目倉庫呢?
          6. lockfile 在 git 操作時,時常會出現(xiàn)大量的沖突,你是怎么解決的呢?

          npm 內(nèi)部機制和背后的思考

          先來看下第一個問題,“刪除 node_modules,重新 npm install” 這樣解決依賴安裝問題百試不爽,其中的原理是什么?這樣做存在怎樣的風險?下面我們一起探究一下。

          npm 的安裝機制非常值得探究。pip 是全局安裝,但 npm 的安裝機制秉承了不同的設(shè)計哲學(xué)。

          npm 會優(yōu)先將依賴包安裝到項目目錄。 這樣做的好處是使不同項目的依賴各成體系,同時還減輕了包作者的 API 壓力;缺點也比較明顯,如果我們的 repo_arepo_b 都有一個相同的依賴 pkg_c,那么這個公共依賴將在兩個項目中各被安裝一次。也就是說,同一個依賴可能在我們的電腦上多次安裝。

          npm install

          上圖是 npm 安裝依賴大致的過程,其中這樣幾個步驟需要關(guān)注:

          1. 檢查配置。包括項目級、用戶級、全局級、內(nèi)置的 .npmrc 文件。
          2. 確定依賴版本,構(gòu)建依賴樹。確定項目依賴版本有兩個來源,一是 package.json 文件,一是 lockfile 文件,兩個確認版本、構(gòu)建依賴樹的來源,互不可少、相輔相成。如果 package-lock.json 文件存在且符合 package.json 聲明的的情況下,直接讀取;否則重新確認依賴的版本。
          3. 下載包資源。下載前先確認本地是否存在匹配的緩存版本,如果有就直接使用緩存文件,如果沒有就下載并添加到緩存,然后將包按依賴樹解壓到 node_modules 目錄。
          4. 生成 lockfile 文件。

          可以確認這樣幾個邏輯:

          1. 構(gòu)建依賴樹的過程中,版本確認需要結(jié)合 package.json 和 package-lock.json 兩個文件。先確認 package-lock.json 安裝版本,符合規(guī)則就以此為準,否則由 package.json 聲明的版本范圍重新確認。特別地,若是在開發(fā)中手動更改包信息,會導(dǎo)致lockfile 版本信息異常,也可能由 package.json 確認。確認好的依賴樹會存到 package-lock.json 文件中,這里跟 yarn.lock 存在差異。
          2. 同一個依賴,更高版本的包會安裝到頂層目錄,即 node_modules 目錄;否則會分散在某些依賴的 node_modules 目錄,如:node_modules/expect-jsx/node_modules/react 目錄。
          3. 如果依賴升級,造成版本不兼容,需要多版本共存,那么仍然是將高版本安裝到頂層,低版本分散到各級目錄。
          4. lockfile 的存在,保證了項目依賴結(jié)構(gòu)的確定性,保障了項目在多環(huán)境運行的穩(wěn)定性。
          5. ...

          yarn 安裝理念以及破解依賴管理困境

          yarn 作為區(qū)別于 npm 的依賴管理工具,誕生之初就是為了解決歷史上 npm 的某些不足,比如 npm 缺乏對于依賴的完整性和一致性保障,以及 npm 安裝速度過慢的問題等,盡管 npm 發(fā)展至今,已經(jīng)在很多方面向 yarn 看齊,但 yarn 的安裝理念仍然需要我們關(guān)注。yarn 提出的安裝理念很好的解決了當時 npm 的依賴管理問題:

          1. 確定性。通過 yarn.lock 等機制,保證了確定性,這里的確定性包括但不限于明確的依賴版本、明確的依賴安裝結(jié)構(gòu)等。即在任何機器和環(huán)境下,都可以以相同的方式被安裝。
          2. 模塊扁平化安裝。將依賴包的不同版本,按照一定策略,歸結(jié)為單個版本,以避免創(chuàng)建多個副本造成冗余。(npm 也有相同的優(yōu)化)
          3. 更好的網(wǎng)絡(luò)性能。Yarn 采用了請求排隊的理念,類似并發(fā)連接池,能夠更好地利用網(wǎng)絡(luò)資源;同時引入了更好的安裝失敗時的重試機制。(npm 較早的版本是順序下載,當?shù)谝粋€包完全下載完成后,才會將下載控制權(quán)交給下一個包)
          4. 引入緩存機制,實現(xiàn)離線策略。(npm 也有類似的優(yōu)化)

          yarn.lock 文件結(jié)構(gòu)

          以 react 等依賴為例,先大致了解一下 yarn.lock 文件的結(jié)構(gòu)以及確定依賴版本的方式:

          # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 
          # yarn lockfile v1 
          expect-jsx@^5.0.0
           version "5.0.0" 
           resolved "[http://registry.npmjs.org/expect-jsx/-/expect-jsx-5.0.0.tgz#61761b43365f285a80eb280c785e0783bbe362c7](http://registry.npmjs.org/expect-jsx/-/expect-jsx-5.0.0.tgz#61761b43365f285a80eb280c785e0783bbe362c7 "http://registry.npmjs.org/expect-jsx/-/expect-jsx-5.0.0.tgz#61761b43365f285a80eb280c785e0783bbe362c7")" 
           integrity sha1-YXYbQzZfKFqA6ygMeF4Hg7vjYsc= 
           dependencies: 
           collapse-white-space "^1.0.0" 
          react "^16.0.0"
           react-element-to-jsx-string "^13.0.0" 
          react-rater@^6.0.0
           version "6.0.0" 
           resolved "[http://registry.npmjs.org/react-rater/-/react-rater-6.0.0.tgz#2e666b6e5e5c33b622541df6a7124f6c99606927](http://registry.npmjs.org/react-rater/-/react-rater-6.0.0.tgz#2e666b6e5e5c33b622541df6a7124f6c99606927 "http://registry.npmjs.org/react-rater/-/react-rater-6.0.0.tgz#2e666b6e5e5c33b622541df6a7124f6c99606927")" 
           integrity sha512-NP1+rEeL3LyJqA5xF7U2fSHpISMcVeMgbQ0u/P1WmayiHccI7Ixx5GohygmJY82g7SxdJnIun2OOB6z8WTExmg== 
           dependencies: 
           prop-types "^15.7.2" 
          react "^16.8.0"
           react-dom "^16.8.0" 
          //一或多個具有相同版本范圍的依賴聲明,確定一個可用的版本。這就是 lockfile 的確定性。
          react@^16.0.0, react@^16.8.0:
          version "16.14.0"
           resolved "[http://registry.npmjs.org/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d](http://registry.npmjs.org/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d "http://registry.npmjs.org/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d")" 
           integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g== 
           dependencies: 
           loose-envify "^1.1.0" 
           object-assign "^4.1.1" 
           prop-types "^15.6.2" 
          //如果同一個依賴存在多個版本,那么最高版本安裝在頂層目錄,即 node_modules 目錄。
          react@^17.0.1:
          version "17.0.2"
           resolved "[http://registry.npmjs.org/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037](http://registry.npmjs.org/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037 "http://registry.npmjs.org/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037")" 
           integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== 
           dependencies: 
           loose-envify "^1.1.0" 
           object-assign "^4.1.1"

          從上面依賴版本描述的信息中,可以確定以下幾點:

          1. 所有依賴,不管是項目聲明的依賴,還是依賴的依賴,都是扁平化管理。
          2. 依賴的版本是由所有依賴的版本聲明范圍確定的,具備相同版本聲明范圍的依賴歸結(jié)為一類,確定一個該范圍下的依賴版本。如果同一個依賴多個版本共存,那么會并列歸類。
          3. 每個依賴確定的版本中,是由以下幾項構(gòu)成:
            1. 多個依賴的聲明版本,且符合 semver 規(guī)范;
            2. 確定的版本號 version 字段;
            3. 版本的完整性驗證字段
            4. 依賴列表
          4. 相比 npm,Yarn 一個顯著區(qū)別是 yarn.lock 中子依賴的版本號不是固定版本。 也就是說單獨一個 yarn.lock 確定不了 node_modules 目錄結(jié)構(gòu),還需要和 package.json 文件進行配合。

          yarn install

          以下是在 yarn 安裝依賴時的步驟:1、檢查(checking)主要是檢查項目中是否存在一些 npm 相關(guān)的配置文件,如 package-lock.json 等。如果存在,可能會警告提示,因為它們可能會存在沖突。在這一階段,也會檢查系統(tǒng) OS、CPU 等信息。

          2、解析包(resolving packages)這一步主要是解析依賴樹,確定版本信息等。首先獲取項目 package.json 中聲明的首層依賴,包括 dependencies, devDependencies, optionalDependencies 聲明的依賴。接著采用遍歷首層依賴的方式獲取依賴包的版本信息,以及遞歸查找每個依賴下嵌套依賴的版本信息,并將解析過和正在解析的包用一個 Set 數(shù)據(jù)結(jié)構(gòu)來存儲,這樣就能保證同一個版本范圍內(nèi)的包不會被重復(fù)解析。

          • 對于沒有解析過的包,首次嘗試從 yarn.lock 中獲取到版本信息,并標記為已解析;
          • 如果在 yarn.lock 中沒有找到包,則向 Registry 發(fā)起請求獲取滿足版本范圍的已知最高版本的包信息,獲取后將當前包標記為已解析。

          總之,在經(jīng)過復(fù)雜的解析算法后,我們就確定了所有依賴的具體版本信息以及下載地址。

          3、獲取包(fetching packages)這一步主要是利用系統(tǒng)緩存,到緩存中找到具體的包資源。首先會嘗試在緩存中查找依賴包,如果沒有命中緩存,則將依賴包下載到緩存中。對于沒有命中緩存的包,Yarn 會維護一個 fetch 隊列,按照規(guī)則進行網(wǎng)絡(luò)請求。這里也是 yarn 誕生之初解決 npm v3 安裝緩慢問題的優(yōu)化點,支持并行下載。

          如何判斷有沒有命中緩存?

          判斷系統(tǒng)中存在符合 "cachefolder+slug+node_modules+pkg.name" 規(guī)則的路徑,如果存在則判斷為命中緩存,否則就會重新下載。值得注意的是,不同版本的包在緩存中是扁平化管理。以下是緩存中 webpack 的依賴緩存,可以通過 yarn cache dir 查看。4、鏈接包(linking dependencies)這一步主要是將緩存中的依賴,復(fù)制到項目目錄下,同時遵循扁平化原則。前面說到,npm 優(yōu)先將依賴安裝到項目目錄,因此需要將全局緩存中的依賴復(fù)制到項目。在復(fù)制依賴前,Yarn 會先解析 peerDependencies,如果找不到符合 peerDependencies 聲明的依賴版本,則進行 warning 提示(這并不會影響命令執(zhí)行),并最終拷貝依賴到項目中。

          5、構(gòu)建包(building fresh package)如果依賴包中存在二進制包需要進行編譯,會在這一步進行。

          如果破解依賴管理困境

          在 npm v2 時期,安裝的依賴會存在于引用依賴的 node_modules 目錄,如果依賴過多,會形成一顆巨大的依賴樹。這種結(jié)構(gòu)雖然簡單明了,但是對于大型項目十分不友好。依賴層級深對開發(fā)排查不利,并且依賴的復(fù)用也是問題。在 npm v3 中引入扁平化的概念。看幾個場景的例子??:

          場景一:不同 npm 版本安裝依賴的結(jié)構(gòu)

          [email protected] 依賴 [email protected],npm v3 是扁平化管理依賴。

          場景二:不同 npm 版本處理依賴多版本共存問題 

          在場景一的基礎(chǔ)上,安裝 [email protected],而它依賴另一個版本的 [email protected]。由于根目錄下已存在 [email protected] 的依賴,npm v3 會把 [email protected] 安裝到 [email protected] 依賴的 node_modules 目錄。

          靚仔疑惑:為什么 [email protected] 在頂級,而 [email protected] 在子級呢?

          場景三:依賴的多版本的數(shù)量與依賴版本分布關(guān)系 

          在場景二的基礎(chǔ)上,安裝 [email protected],而它也依賴 [email protected]。同樣的,由于根目錄下已存在 [email protected] 的依賴,npm v3 會把 [email protected] 安裝到 [email protected] 依賴的 node_modules 目錄。

          靚仔疑惑:你可能會疑問,此時存在2個 [email protected] 和1個 [email protected],出現(xiàn)在頂級安裝目錄的不應(yīng)該是 v2 版本而非 v1 版本嘛?

          其實這是由依賴的安裝順序決定的,真就是依賴的某個版本如果出現(xiàn)在合適的時間,那么它就會被安裝到頂級 node_modules 目錄。不同版本的出場順序?qū)е乱蕾嚱Y(jié)構(gòu)的差異,npm v3 注定不是穩(wěn)定的包管理工具。跟生活一樣,人物的出場順序很重要,它決定了你在哪里做什么事。

          場景四:依賴版本存在重復(fù)和可用 

          在場景三的基礎(chǔ)上,安裝 [email protected],它依賴 [email protected]。由于頂級目錄已存在目標版本,因此 npm v3 會跳過該依賴的安裝。

          場景五:版本升級囧境在

          場景三的基礎(chǔ)上,如果更新了 [email protected],同時它的依賴是 [email protected]。那么 npm v3 的執(zhí)行順序是,刪除 [email protected],安裝 [email protected],安裝 [email protected],留下了 [email protected] 在頂層目錄,因此 [email protected] 會安裝到其父依賴的 node_modules 目錄。

          場景六:依賴版本多目錄存在且符合復(fù)用條件 

          在場景五的基礎(chǔ)上,更新 [email protected],它依賴了 [email protected]。那么 npm v3 的執(zhí)行順序是,刪除 [email protected],安裝 [email protected],刪除 [email protected],安裝 [email protected],于是出現(xiàn)以下結(jié)構(gòu)。

          此時你會發(fā)現(xiàn),存在多個 [email protected] 分布在不同的 node_modules 目錄,他們是不是只要在頂級目錄存在一份即可?沒錯,我們刪除 node_modules 目錄重裝,得到的就是你想的清晰的結(jié)構(gòu)。實際上,更優(yōu)雅的方式是使用 npm dedupe 命令達到上述結(jié)構(gòu)。而 yarn 在安裝依賴時會自動執(zhí)行 dedupe 命令。

          正是由于上述一些 npm 歷史的坑,所以更建議使用 yarn 作為項目協(xié)作的包管理工具。當然 npm 發(fā)展至今,很多問題已經(jīng)優(yōu)化掉,現(xiàn)在 yarn 和 npm 是兩款互相看齊、互相獲取靈感的依賴管理工具。

          npm vs. yarn ??

          這里簡單對比 npm v6 和 yarn v1. 這是我們生產(chǎn)開發(fā)常用的版本。

          npm 和 yarn 作為兩款相似的包管理工具,在一些功能實現(xiàn)上它們互相獲取靈感。

          相同點:

          1. package.json 作為項目依賴描述文件。
          2. node_modules 作為依賴存儲目錄,yarn v2 不再是這樣。
          3. lockfile 鎖定版本依賴,在 yarn 中叫 yarn.lock,在 npm 中叫 package-lock.json,在 npm v7 也支持了 yarn.lock。它確保在不同機器或不同環(huán)境中,能夠得到穩(wěn)定的 node_modules 目錄結(jié)構(gòu)。

          差異:

          1. 依賴管理策略。
          2. lockfile。package-lock.json 自帶版本鎖定+依賴結(jié)構(gòu),你想改動一些依賴,可能影響的范圍要比表面看起來的復(fù)雜的多;而 yarn.lock 自帶版本鎖定,并沒有確定的依賴結(jié)構(gòu),使用 yarn 管理項目依賴,需要 package.json + yarn.lock 共同確定依賴的結(jié)構(gòu)。
          3. 性能。(對比 npm v6 和 yarn v1)目前 npm v7 優(yōu)化了緩存和下載網(wǎng)絡(luò)策略,性能的差異在縮小。

          [拓展] npm 企業(yè)級部署私服原理

          npm 中的源(registry),其實就是一個查詢服務(wù)。以 npmjs.org 為例,它的查詢服務(wù)網(wǎng)址是 https://registry.npmjs.org/ ,在這個網(wǎng)址后加上依賴的名字,就會得到一個 JSON 對象,里面包含了依賴所有的信息。例如:

          • https://registry.npmjs.org/react
          • https://registry.npm.taobao.org/react

          我們可以通過 npm config set registry 命令來設(shè)置安裝源。你知道我們公司為什么要部署私有的 npm 鏡像嗎?雖然 npm 并沒有被屏蔽,但是下載第三方依賴包的速度依然較緩慢,這嚴重影響 CI/CD 流程或本地開發(fā)效率。通常我們認為部署 npm 私服具備以下優(yōu)點:

          1. 確保高速、穩(wěn)定的 npm 服務(wù)
          2. 確保發(fā)布私有模塊的安全性
          3. 審核機制可以保障私服上 npm 模塊質(zhì)量和安全

          部署企業(yè)級私服,能夠獲得安全、穩(wěn)定、高速的保障。

          管理項目依賴的小技巧(集思廣益...)

          1. 推薦使用 yarn 作為團隊包管理工具,而不是 npm。盡管在 npm v6 之后的版本趨向穩(wěn)定和安全,但由于歷史原因和團隊管理兼容性,仍然是推薦使用 yarn 作為團隊統(tǒng)一的包管理工具。
          2. 項目中一定要存在 lockfile 文件,且禁止手動修改,因為這是項目穩(wěn)定性運行的保障。
          3. 如果 yarn.lock 在代碼合并的過程中出現(xiàn)了問題,可以嘗試使用 yarn install 解決問題。
          4. ...

          參考資料

          • npm install[1]
          • yarn install[2]
          • yarn.lock[3]
          • NPM vs. Yarn: Which Package Manager Should You Choose?[4]
          • Lockfiles should be committed on all projects[5]

          參考資料

          [1]

          npm install: https://docs.npmjs.com/cli/v7/commands/npm-install

          [2]

          yarn install: https://classic.yarnpkg.com/en/docs/cli/install

          [3]

          yarn.lock: https://classic.yarnpkg.com/en/docs/yarn-lock

          [4]

          NPM vs. Yarn: Which Package Manager Should You Choose?: https://www.whitesourcesoftware.com/free-developer-tools/blog/npm-vs-yarn-which-should-you-choose/

          [5]

          Lockfiles should be committed on all projects: https://classic.yarnpkg.com/blog/2016/11/24/lockfiles-for-all/

          ?? 謝謝支持

          以上便是本次分享的全部內(nèi)容,希望對你有所幫助^_^

          喜歡的話別忘了 分享、點贊、收藏 三連哦~。

          歡迎關(guān)注公眾號 前端Sharing 收貨大廠一手好文章~


          瀏覽 62
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩 中文 欧美 | 大香蕉精品在线视频 | 中文字幕五月天 | 亚洲视频一区二区 | 国产三级网站免费观看 |