• npm、cnpm、yarn、pnpm


    安装

    1、安装npm需要安装nodejs,node中自带npm包管理器

    node下载地址:下载 | Node.js

    2、cnpm安装(需要安装npm)

    cnpm是淘宝团队做的npm镜像,淘宝镜像每 10分钟 进行一次同步以保证尽量与官方服务同步。

    npm install -g cnpm --registry=https://registry.npm.taobao.org

    3、yarn安装(需要安装npm)

    npm install -g yarn

    官网地址:安装 | Yarn 中文文档

    4、pnpm安装(需要安装npm)

    npm install -g pnpm

    官网地址:安装 | pnpm 中文文档 | pnpm 中文网

    npm和yarn的比较

    1、并行安装:yarn安装包会同时执行多个任务,npm 需等待上一个任务安装完成才能运行下一个任务(按照在package.json中声明的顺序),所以npm install 下载速度慢,即使是重新 install 时速度依旧慢

    2、离线模式:如果你已经安装过一个包,用 yarn 再次安装会从缓存中获取,而 npm 会从网络下载

    3、版本锁定:yarn 默认有一个 yarn.lock 文件锁定版本,保证环境统一,npm是通过

    package-lock.json

    4、更简洁的输出:yarn 安装包时输出的信息较少,npm 输出信息冗余

    5、常用指令对比:

    npm和yarn同时混用会有什么问题

    当我们运行npm i lodash --savelodash将会被当做依赖加入到package.json中:

    1. "dependencies": {
    2. "loadsh": "^4.17.4"
    3. }

    由于package.json文件中的版本号的特点,下面的三个版本号在安装的时候就代表不同的含义。

    • "6.0.3"表示安装指定6.0.3版本,
    • "~6.0.3"表示安装6.0.X中最新的版本,
    • "^6.0.3"表示安装6.X.X中最新的版本,

    举个例子:以typescript依赖包为例

    1、原项目中使用package.json定义项目中需要依赖的包,这里的typescript版本号为^4.2.4

    2、原项目是用npm来进行包管理,从而生成package-lock.json文件,里面存储了各个依赖的具体来源和版本号,其中typescript的版本号为4.2.4,所以今后使用npm进行安装依赖时都会安装typescript的4.2.4版本,不会进行自动升级

    3、如开发者使用yarn命令来进行包依赖安装,则package-lock.json文件无效,只看package.json中的文件,但typescript版本号为^4.2.4,从而会安装4.x.x版本中最新版本即为4.6.3版本,同时生成对应yarn.lock文件

    4、启动项目会出现typescript类型报错,其原因是因为原项目是在4.2.4typescript版本环境下编写,但使用yarn进行依赖安装把typescript版本自动更新成了 4.6.3版本,同时4.2.4和4.6.3版本的typescript在类型校验上进行了较大的改进和优化,可以检测出更多的类型问题

    所以,如有yarn.lock文件而没有package-lock.json文件则项目是以yarn 来进行包管理;

    如有package-lock.json文件而没有yarn.lock文件则项目是以npm来进行包管理。

     参考文章:同项目混用npm和yarn出现版本自动更新问题_大狼狗的博客-CSDN博客_npm yarn 混用

    pnpm

    1、在npm3以前,采用的是嵌套安装的方式。假如我们安装依赖B和C时,如果B和C依赖中依赖了 packageD,那么会将 packageD重复安装2次。这将导致 会占据比较大的磁盘空间,且 windows 的文件路径最长是 260 多个字符,这样嵌套是会超过 windows 路径的长度限制的。

     

    2、npm3+和yarn采取铺平的方式,将依赖扁平化,所有的依赖不再一层层嵌套了,而是全部在同一层,这样也就没有依赖重复多次的问题了,也就没有路径过长的问题了。

     

    为什么会采取这样的方式呢?当我们运行require("xxx")引入外部模块时,

    • 如果xxx是一个node核心模块,例如fshttp等,那么返回node核心模块。
    • 如果不是,那么会判断判断当前node_modules 文件夹是否有此模块,如果有就返回,如果没有就递归往上层的node_modules目录查找,如果找到就返回,如果到根目录都没找到就报错。

    所以当依赖B中的代码使用require("A")的时候,找不到A,会往上层的文件夹的node_modules中继续寻找,所以利用node的require机制,我们可以尽可能的把复用的依赖提升到最上层

    a:这样貌似解决了我们的问题,事实上并没有而且引入了更大的问题幽灵依赖(node_modules 中的依赖包在没有 package.json 中声明的情况下使用了其他包的依赖)

    如果 A 依赖 B, B 依赖 C,那么 A 当中是可以直接使用 C 的,但问题是 A 当中并没有声明 C 这个依赖。因此会出现这种非法访问的情况。因为没有显式依赖,万一有一天B不依赖C了,那你的代码也就不能跑了,因为你依赖的这个包C,但是现在不会被安装了。

    b:除此之外:一个包是可能有多个版本的,提升只能提升一个,所以后面再遇到相同包的不同版本,依然还是用嵌套的方式。这就是依赖结构的不确定性。比如:如果提升了packageD2.0.0,依赖B和依赖C同时依赖了packageD1.0.0,那么D1.0.0还是会被安装多次。当两个不同的组件调用 packageD 时,它们可能会得到两个不同的库实例,这意味着可能会突然出现两个单例的实例.避免这个问题的解决方案:lock 文件

    3、pnpm

    Fast, disk space efficient package manager | pnpm

    前置知识:

    inode:每一个文件都有一个唯一的 inode,它包含文件的元信息,在访问文件时,对应的元信息会被 copy 到内存去实现文件的访问。

    hard link:硬链接可以理解为是一个相互的指针,创建的 hardlink 指向源文件的 inode,系统并不为它重新分配 inode。硬链接不管有多少个,都指向的是同一个 inode 节点,这意味着当你修改源文件或者链接文件的时候,都会做同步的修改。每新建一个 hardlink 会把节点连接数增加,只要节点的链接数非零,文件就一直存在,不管你删除的是源文件还是 hradlink。只要有一个存在,文件就存在。(同一个文件的不同引用)

    soft link:软链接可以理解为是一个单向指针,是一个独立的文件且拥有独立的 inode,永远指向源文件,这就类比于 Windows 系统的快捷方式。删除源文件,软链接就会失效。修改了软链接或硬链接的文件,另外的硬链接或软链接以及源文件都会发生变化,这里感觉是需要小心的,特别是修改文件以调试的时候,记得还原回去,否则另外一个项目用到的时候,可能会出问题。(新建一个文件,文件内容指向另一个路径)

    .pnpm

    虚拟存储目录——.pnpm,所有直接和间接依赖项都链接到此目录中。该目录通过 @ 来实现相同模块不同版本之间隔离和复用

    Store

    pnpm在全局通过Store来存储所有的 node_modules 依赖,并且在 .pnpm 中存储项目的hard links

    在使用 pnpm 对项目安装依赖的时候,如果某个依赖在 sotre 目录中存在了话,那么就会直接从 store 目录里面去 hard-link,避免了二次安装带来的时间消耗,如果依赖在 store 目录里面不存在的话,就会去下载一次。

    假如全局的包变得非常大怎么办?使用方法为 pnpm store prune ,它提供了一种用于删除一些不被全局项目所引用到的 packages 的功能,例如有个包 axios@1.0.0 被一个项目所引用了,但是某次修改使得项目里这个包被更新到了 1.0.1 ,那么 store 里面的 1.0.0 的 axios 就就成了个不被引用的包,执行 pnpm store prune 就可以在 store 里面删掉它了。

    原理分析

    我们项目中有一个依赖 bar@1.0.0。bar@1.0.0也有一个依赖 foo@1.0.0。

    • node_modules 下面有 bar@1.0.0 和 .pnpm 目录,没有 foo@1.0.0

    • bar@1.0.0 通过软链接指向 .pnpm/bar@1.0.0/node_modules/bar@1.0.0.pnpm/bar@1.0.0/node_modules/bar@1.0.0 又通过硬链接指向 Store

    • bar@1.0.0 依赖的foo@1.0.0 会安装在跟自己的同一级,这里的设计,我理解是根据 node 的 require 机制,bar 中 require('foo') 的时候,就会先找到 foo@1.0.0,而不会往上寻找,这样就避免依赖包版本不一致的问题。.pnpm/bar@1.0.0/node_modules/foo@1.0.0。并通过软链接指向 .pnpm 下一级的 foo@1.0.0

    • .pnpm/foo@1.0.0 一样通过硬链接指向 Store

    参考文章:pnpm 是凭什么对 npm 和 yarn 降维打击的 - 掘金

    从npm 到 yarn 再到 pnpm —— 为什么要使用pnpm? - 掘金

    最高性能的包管理器-pnpm

  • 相关阅读:
    怎样做好金融投资翻译
    一款优秀的智慧社区系统应具备哪些功能
    代码随想录算法训练营第十三天|n皇后&数独老经典了
    线性表之顺序表
    Spring容器加载Bean和JVM加载类
    深度学习笔记之受限玻尔兹曼机(一)玻尔兹曼分布介绍
    基于python+Django深度学习的音乐推荐方法研究系统设计与实现
    有钱还系统源码 人人还众筹还钱模式还贷系统源码
    html旅游网站设计与实现——绿色古典旅游景区 HTML+CSS+JavaScript
    模拟经营类游戏是怎么开发的?
  • 原文地址:https://blog.csdn.net/qq_39290323/article/details/126486101