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

2022-08-12  本文已影响0人  470d98b91bd3

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@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的严格模式能够帮助我们避免一些低级错误。正常情况下,是不推荐使用羞耻提升的。

上一篇下一篇

猜你喜欢

热点阅读