美文网首页
JavaScript常见面试题:npm跟pnpm有什么区别?

JavaScript常见面试题:npm跟pnpm有什么区别?

作者: 470d98b91bd3 | 来源:发表于2022-08-12 00:20 被阅读0次

    npm的发展

    最早期的npm

    早期的npm的依赖会被嵌套安装,也就是说:

    {
      "dependencies": {
        "A": "^1.0",
        "B": "^1.0",
        "C": "^1.0"
      }
    }
    
    

    如果我A,B,C三个包均引用了D包,但是A、B引用的是D@1.0.0,而C引用的是D@2.0.0,他们会分别安装到自己的node_modules底下。

    // 项目的根node_modules
    node_modules
         A
               node_modules
                      D@1.0.0
         B
               node_modules
                      D@1.0.0
         C
               node_modules
                      D@2.0.0
    
    

    但是这样会导致依赖地狱。会出现依赖路径过长、以及文件被多次复制的问题!

    npm3

    为了解决依赖路径过长的问题,在npm3之后,依赖就被扁平化管理了。依赖被顶到了顶层,但是当出现上面的情况的时候,依赖的表现是怎么样的?

    这时候先安装的包,会把他依赖的相应版本提前,后面安装的D包如果版本跟被置顶的版本号不一致,会被安装到其node_modules下。

    // 项目的根node_modules
    node_modules
         A
         B
         C
               node_modules
                      D@2.0.0
         D(@1.0.0 )
    
    

    但是这个会出现一个问题,就是如果根据安装的顺序进行依赖提升,用户在npm i的时候,得到的结果是不确定的。因为npm也做了相对应的优化,把引用次数多的包扁平化管理,但当两个引用次数一样的时候,那必然带来的不确定性

    npm5

    为了解决上面的问题,在package.json的基础上,又新增了 package-lock.json 文件。

    虽然v5.0.x跟v5.1.0的版本不一样,我们无需记住这个,只需要稍微了解即可。

    • npm@5.0.x 里,不管package.json怎么变,npm i都会根据lock文件下载。

    • npm@5.1.0版本后,npm i会无视package-lock.json文件,直接下载新的npm包;

    • npm@5.4.2版本后,如果package.json和package.lock文件不同那么,npm i时会根据package的版本进行下载并更新package-lock;如果两个文件相同则会根据package-lock文件下载,不管package有无更新

    但是尽管这样,他会有幽灵依赖的问题。

    幽灵依赖

    幽灵依赖在npm@3.x的版本中就已经出现了,因为有了提升的特性,上述例子中,虽然项目中没有在package.json中显性声明要安装D@1.0.0,但是npm已经将他提升到根部,此时在项目中引用D并进行使用是不会报错的。但是由于我们没有显性声明,假如一旦依赖A不再依赖D或者版本有变化那么此时install后代码就会因为找不到依赖而报错!!!

    当然,npm还有另一个问题,就是依赖分身。比如我们A,B引用的是D@1.0.0,而C,E引用的是D@2.0.0,项目中D@1.0.0已经被依赖提升到顶部了,那么C,E的node_modules种均会有 D@2.0.0 的依赖,因此他会被重复安装。

    pnpm

    pnpm 号称 performance npm,与npm的依赖提升和扁平化不同。pnpm采取了一套新的策略:内容寻址储存

    还是使用上面的例子: 项目依赖了A、B、C,之后A依赖D@1.0,B依赖D@2.0,而C也依赖D@1.0,使用 pnpm 安装依赖后 node_modules 结构如下

    // 项目的根node_modules
    node_modules
         .pnpm
               A@1.0.0
                      node_modules
                           A => <store>/A@1.0.0
                           D => ../../D@1.0.0
               D@1.0.0
                      node_modules
                            D => <store>/D@1.0.0
               B@1.0.0
                      node_modules
                           B => <store>/B@1.0.0
                           D => ../../D@2.0.0
               C@1.0.0
                    node_modules
                         C => <store>/C@1.0.0
                         D => ../../D@1.0.0
          A => .pnpm/A@1.0.0/node_modules/A
          B => .pnpm/B@1.0.0/node_modules/B
          C => .pnpm/C@1.0.0/node_modules/C
    
    

    我们看到,pnpm拥有自己的.pnpm目录,他会以平铺的方式来存储所有包,以依赖名加上版本号的名字为命名,实现了版本的复用。而且他不是通过拷贝机器缓存中的依赖到项目目录下,而是通过硬链接的方式,这能减少空间占用。

    至于根目录下用于项目使用的依赖,则是通过符号链接的方式,链接到它的 .pnpm 目录下的对应位置。

    shamefully-hosit

    默认情况下,通过pnpm的node_modules你只能访问到在 package.json 文件中声明的依赖,只有依赖项才能访问未声明的依赖项。你可能需要需要再.npmrc文件中声明了 shamefully-host=true,他才会像npm平铺的方式,我们才能使用package.json没有显性声明的幽灵依赖。

    不过事实上,pnpm的严格模式能够帮助我们避免一些低级错误。正常情况下,是不推荐使用羞耻提升的。

    相关文章

      网友评论

          本文标题:JavaScript常见面试题:npm跟pnpm有什么区别?

          本文链接:https://www.haomeiwen.com/subject/efcxgrtx.html