<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>

          請你實現(xiàn)一個 npm install

          共 6527字,需瀏覽 14分鐘

           ·

          2021-06-05 18:31

          現(xiàn)在寫代碼我們一般不會全部自己實現(xiàn),更多是基于第三方的包來進行開發(fā),這體現(xiàn)在目錄上就是 src 和 node_modules 目錄。

          src 和 node_modules(第三方包) 的比例不同項目不一樣。

          運行時查找第三方包的方式也不一樣:

          • 在 node 環(huán)境里面,運行時就支持 node_modules 的查找。所以只需要部署 src 部分,然后安裝相關(guān)的依賴。
          • 在瀏覽器環(huán)境里面不支持 node_modules,需要把它們打包成瀏覽器支持的形式。

          跨端環(huán)境下,它是上面哪一種呢?

          都不是,不同跨端引擎的實現(xiàn)會有不同,跨端引擎會實現(xiàn) require,可以運行時查找模塊(內(nèi)置的和第三方的),但是不是 node 的查找方式,是自己的一套。

          和 node 環(huán)境下的模塊查找類似,但是目錄結(jié)構(gòu)不一樣,所以需要自己實現(xiàn) xxx install。

          思路分析

          npm 是有自己的 registry server 來支持 release 的包的下載,下載時是從 registry server 上下載。我們自己實現(xiàn)的話沒必要實現(xiàn)這一套,直接用 git clone 從 gitlab 上下載源碼即可。

          依賴分析

          要實現(xiàn)下載就要先確定哪些要下載,確定依賴的方式和打包工具不同:

          • 打包工具通過 AST 分析文件內(nèi)容確定依賴關(guān)系,進行打包
          • 依賴安裝工具通過用戶聲明的依賴文件 (package.json / bundle.json)來確定依賴關(guān)系,進行安裝

          這里我們把包的描述文件叫做 bundle.json,其中聲明依賴的包:

          {
              "name""xxx",
              "dependencies": {
                  "yyyy""aaaa/bbbb#release/1111"
              }
          }

          通過分析項目根目錄的 bundle.json 作為入口,下載每一個依賴,分析 bundle.json,然后繼續(xù)下載每一個依賴項,遞歸這個過程。這就是依賴分析的過程。

          這樣依賴分析的過程中進行包的下載,依賴分析結(jié)束,包的下載也就結(jié)束了。這是一種可行的思路。

          但是這種思路存在問題,比如:版本沖突怎么辦?循環(huán)依賴怎么辦?

          解決版本沖突

          版本沖突是多個包依賴了同一個包,但是依賴的版本不同,這時候就要選擇一個版本來安裝,我們可以簡單的把規(guī)則定為使用高版本的那個。

          解決循環(huán)依賴

          包之間是可能有循環(huán)依賴的(這也是為什么叫做依賴圖,而不是依賴樹),這種問題的解決方式就是記錄下處理過的包,如果同個版本的包被分析過,那么久不再進行分析,直接拿緩存。

          這種思路是解決循環(huán)依賴問題的通用思路。

          我們解決了版本沖突和循環(huán)依賴的問題,還有沒有別的問題?

          版本沖突時會下載版本最高的包,但是這時候之前的低版本的包已經(jīng)下載過了,那么就多了沒必要的下載,能不能把這部分冗余下載去掉。

          依賴分析和下載分離

          多下載了一些低版本的包的原因是我們在依賴分析的過程中進行了下載,那么能不能依賴分析的時候只下載 bundle.json 來做分析,分析完確定了依賴圖之后再去批量下載依賴?

          從 gitlab 上只下載 bundle.json 這一個文件需要通過 ssh 協(xié)議來下載,略微復(fù)雜,我們可以用一種更簡單的思路來實現(xiàn):

          git clone --depth=1 --branch=bb xxx

          加上 --depth 以后 git clone 只會下載單個 commit,速度會很快,雖然比不上只下載 bundle.json,但是也是可用的(我試過下載全部 commit 要 20s 的時候,下載單個 commit 只要 1s)。

          這樣我們在依賴分析的時候只下載一個 commit 到臨時目錄,分析依賴、解決沖突,確定了依賴圖之后,再去批量下載,這時候用 git clone 下載全部的 commit。最后要把臨時目錄刪除。

          這樣,通過分離依賴分析和下載,我們?nèi)サ袅藳]必要的一些低版本包的下載。下載速度會得到一些提升。

          全局緩存

          當(dāng)本地有多個項目的時候,每個項目都是獨立下載自己的依賴包的,這樣對于一些公用的包會存在重復(fù)下載,解決方式是全局緩存。

          分析完依賴進行下載每一個依賴包的時候,首先查找全局有沒有這個包,如果有的話,直接復(fù)制過來,拉取下最新代碼。如果沒有的話,先下載到全局,然后復(fù)制到本地目錄。

          通過多了一層全局緩存,我們實現(xiàn)了跨項目的依賴包復(fù)用。

          代碼實現(xiàn)

          為了思路更清晰,下面會寫偽代碼

          依賴分析

          依賴分析會遞歸處理 bundle.json,分析依賴并下載到臨時目錄,記錄分析出的依賴。會解決版本沖突、循環(huán)依賴問題。

          const allDeps = {};
          function installDeps(projectDir{
              const bundleJsonPath = path.resolve(projectDir, 'bundle.json');
              const bundleInfo = JSON.parse(fs.readFileSync(bundleJsonPath));
              
              const bundleDeps = bundleInfo.dependencies;
              for (let depName in bundleDeps) {
                  if(allDeps[depName]) {
                      if (allDeps[depName] 和 bundleDeps[depName] 分支和版本一樣) {
                          continue;// 跳過安裝
                      }
                      if (allDeps[depName] 和 bundleDeps[depName] 分支和版本不一樣){
                          if (bundleDeps[depName] 版本 < allDeps[depName] 版本 ) {
                              continue;
                          } else {
                              // 記錄下版本沖突
                              allDeps[depName].conflit = true;
                          }
                     
                      }
                  }
                  childProcess.exec(`git clone --depth=1 ${臨時目錄/depName}`);
                  allDeps[depName] = {
                      name: depName
                      url: xxx
                      branch: xxx
                      version: xxx
                  }
                  installDeps(`${臨時目錄/
          depName}
          `
          );
              }    
          }

          下載

          下載會基于上面分析出的 allDeps 批量下載依賴,首先下載到全局緩存目錄,然后復(fù)制到本地。

          function batchInstall(allDeps) {
              allDeps.forEach(dep => {
                  const 全局目錄 = path.resolve(os.homedir(), '.xxx');
                  if (全局目錄/dep.name 存在) {
                      // 復(fù)制到本地
                      childProcess.exec(`cp 全局目錄/dep.name 本地目錄/dep.name`);
                  } else {
                      // 下載到全局
                      childProcess.exec(`git clone --depth=1 ${全局目錄/dep.name}`);
                       // 復(fù)制到本地
                      childProcess.exec(`cp 全局目錄/dep.name 本地目錄/dep.name`);
                  }
              });
          }

          這樣,我們就完成了依賴的分析和下載,實現(xiàn)了全局緩存。

          總結(jié)

          我們首先梳理了不同環(huán)境(瀏覽器、node、跨端引擎)對于第三方包的處理方式不同,瀏覽器需要打包,node 是運行時查找,跨端引擎也是運行時查找,但是用自己實現(xiàn)的一套機制。

          然后明確了打包工具確定依賴的方式是 AST 分析,而依賴下載工具則是基于包描述文件 bundl.json(package.json) 來分析。然后我們實現(xiàn)了遞歸的依賴分析,解決了版本沖突、循環(huán)依賴問題。

          為了減少沒必要的下載,我們做了依賴分析和下載的分離,依賴分析階段只下載單個 commit,后續(xù)批量下載的時候才全部下載。下載方式?jīng)]有實現(xiàn) registry 的那套,而是直接從 gitlab 來 git clone。

          為了避免多個項目的公共依賴的重復(fù)下載,我們實現(xiàn)了全局緩存,先下載到全局目錄,然后再復(fù)制到本地。

          npm install、yarn install 的實現(xiàn)流程細節(jié)會更多一些,但是整體流程類似。希望這篇文章能幫你梳理清楚思路:不同環(huán)境是怎么處理第三方包的,xxx install 的依賴分析和下載的流程是什么樣的。


          瀏覽 41
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  俺也去电影网 | 日韩毛片观看 | 色导航在线 | 天天肏天天操 | 青青草 婷婷 |