跳至主要內容

前端包管理器

LincZero大约 13 分钟

前端包管理器

node包管理器

各种分支

npm(node package manager,node的包管理工具)

node package manager,下载更新node的时候会自动下载更新npm

一些常用命令

npm install # 根据 package.json 自动安装
npm install xxx # 或 npm i xxx
npm uninstall xxx

一些选项

  • --save-S,添加到 package.json 的 dependencies 列表,运行时依赖
  • --save-dev-D,添加到 package.json 的 depDependencies 列表,开发时依赖
  • --global-g,安装到全局而不是本地
  • @type/...,配合TypeScript使用时,我们通常需要为第三方库安装类型定义文件。这可以使TypeScript理解库的类型并提供更好的智能感知。 另外,@type包一般属于开发时依赖,一般会使用 --save-dev 进行安装
  • ...@next,表示下一个预发布版本
  • ...@latest,表示最近的稳定版本,注意不是last而是latest,都有“最近的”的意思

cnpm(淘宝npm镜像)

同步频率目前为 10分钟 一次以保证尽量与官方服务同步

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

cnpm支持npm除了publish之外的所有命令,所以说你可以放心的使用cnpm来代替npm使用,这样可能使用包管理的时候更加的方便,因为服务器就在国内。

npx

参考:【掘金】npm 与 npx 什么关系?又有哪些区别?open in new window

  • npm
    • 本身不能够执行任何包,对于本地项目的包,如果想要执行,则需要写入到 package.json 里面,然后通过 npm 来解析 package.json 文件,解析到包的 bin 文件路径,在 bash 中执行。
  • npx
    • 是一个工具,npm v5.2.0引入的一条命令(npx),一个npm包执行器,指在提高从npm注册表使用软件包时的体验 ,npm使得它非常容易地安装和管理托管在注册表上的依赖项,npx使得使用CLI工具和其他托管在注册表。
    • 主要特点:
      1. 临时安装可执行依赖包,不用全局安装,不用担心长期的污染
      2. 可以执行依赖包中的命令,安装完成自动运行
      3. 自动加载node_modules中依赖包,不用指定$PATH
      4. 可以指定node版本、命令的版本,解决了不同项目使用不同版本的命令的问题
  • 总结
    • npx 为 npm 文件执行功能进行了一些扩展,提供了一种使用 node.js CLI 工具的新方式,这种方式比起以前的执行方式更加灵活,并且对于本地环境变量的污染更小(你可以到自己的 node 安装目录下看一下,有多少个 bin 目录下的软连接在污染你的环境变量)。指定包版本的功能可以让我们更灵活地测试一些新的功能,而不用进行升级和降级,还是能够带来很多方便的。

pnpm

快速的,节省磁盘空间的包管理工具。

官网中文说明:https://pnpm.io/zh/

yarn

使用

Yarn和npm命令对比

- < npm                       | (simp)        | yarn
- npm install                 | npm i         | yarn
- npm install taco --save     | npm i taco    | yarn add taco
- npm uninstall taco --save   | npm un taco   | yarn remove taco
- npm install taco --save-dev | npm i -D taco | yarn add taco --dev
- npm update --save           | npm up        | yarn upgrade
与Npm区别

参考:https://zhuanlan.zhihu.com/p/27449990

“Yarn是由Facebook、Google、Exponent 和 Tilde 联合推出了一个新的 JS 包管理工具 ,正如官方文档open in new window中写的,Yarn 是为了弥补 npm 的一些缺陷而出现的。”

npm时的坑了:

  • npm install的时候巨慢。特别是新的项目拉下来要等半天,删除node_modules,重新install的时候依旧如此。

  • 同一个项目,安装的时候无法保持一致性。由于package.json文件中版本号的特点,下面三个版本号在安装的时候代表不同的含义。

    "5.0.3",
    "~5.0.3",
    "^5.0.3"
    

Yarn的优点:

  • 速度快

    • 并行安装

    • 离线模式

    • 安装版本统一

    • 更简洁的输出

    • 多注册来源处理

    • 更好的语义化

不过有了yarn的压力之后,npm做了一些类似的改进。

  1. 默认新增了类似yarn.lock的 package-lock.json;
  2. git 依赖支持优化:这个特性在需要安装大量内部项目(例如在没有自建源的内网开发),或需要使用某些依赖的未发布版本时很有用。在这之前可能需要使用指定 commit*id 的方式来控制版本。
  3. 文件依赖优化:在之前的版本,如果将本地目录作为依赖来安装,将会把文件目录作为副本拷贝到 node*modules 中。而在 npm5 中,将改为使用创建 symlinks 的方式来实现(使用本地 tarball 包除外),而不再执行文件拷贝。这将会提升安装速度。目前yarn还不支持。

总结

在npm5.0之前,yarn的优势特别明显。但是在npm之后,通过以上一系列对比,我们可以看到 npm5 在速度和使用上确实有了很大提升,值得尝试,不过还没有超过yarn。

比较

GPT4


  • npm(Node Package Manager)
    • Node.js 默认的包管理器,也是全球最大的软件注册表
    • 优点
      • npm在Node.js安装的时候默认就已经包含了
      • 提供npm scripts,一个非常有用的工具,可以帮助你运行和构建应用
    • 缺点
      • 国内时有网络问题,有鉴于此,国内出现了一些npm的镜像网站,例如cnpm
      • 在npm v5之前,npm没有提供package-lock功能,意味着两次执行npm install可能会产生不同的node_modules
  • cnpm
    • cnpm是淘宝团队对npm进行一些改进后的版本,主要解决的是npm在中国大陆访问速度慢的问题。使用cnpm镜像可以大大提高npm模块的安装速度。
    • 优点
      • 安装速度快
      • 安装的模块版本更稳定
    • 缺点
      • 不能直接使用npm的官方插件,需要通过插件的作者提供支持
  • npx
    • npx是npm v5.2.0引入的一种工具,可以帮助开发者更简单方便的运行npm包
    • 优点
      • 无需全局安装npm包,可以直接调用
      • 可以直接从GitHub或者其他地方运行代码
      • 可以指定不同版本的Node.js运行代码
    • 缺点
      • 使用npx可能会导致工作路径包含非常大的.node_modules目录
      • 在某些情况下,npx可能不知道应该执行哪个命令
  • pnpm
    • pnpm是一个值得关注的npm客户端,它的安装策略与npm和yarn不同,pnpm通过使用硬链接和符号链接以一种新的方式存储模块
    • 优点
      • 节省磁盘空间和安装速度快
      • 严格的包隔离,一个包只能访问它在package.json中声明的依赖关系,这使编写可移植和可靠的代码更加容易
    • 缺点
      • 由于其存储策略的特殊性,可能在一些项目中引发异常
  • yarn
    • yarn是由Facebook在2016年推出的,为了解决npm的一些问题,例如安装速度慢,package-lock.json不始终可靠等。由于yarn可以并行处理依赖关系,安装速度比npm快很多。并且yarn提供了一个稳定的lockfile和更直观的CLI输出等特性。
    • 优点
      • 安装速度快
      • 默认生成lock文件,确保了依赖版本的一致性
      • 可以并行执行任务,提高运行和安装速度
    • 缺点
      • 所需内存大
      • 需要与你的项目和团队同步,并且需要新的配置步骤

冲突问题(FAQ)

GPT4


  1. 同时使用是否会有冲突?比如我已经选择了pnpm或npx,但遇到了一个库的官方推荐下载方法是npm,此时我是否能用回npm而不造成任何冲突?或反过来之前用npm,但有一个包他推荐用npx(yarn好像会和另外几个冲突?)

  2. 除了镜像,他们的源都是npm网站上的吗?能保证来源的一致性?


  1. 使用不同的包管理器可能会引发一些问题,但这通常是可以管理的。 例如,如果你使用pnpm或yarn,但是又需要使用npm安装某个包,是可以的。

    只要注意管理好你的node_modules和package-lock文件以避免冲突。在相似的情形下,有一个包推荐使用npx,你也可以无冲突的使用,因为npx主要是用来执行命令,而不是管理项目的依赖性。关于yarn和其它包管理工具可能出现的问题,主要原因在于yarn有自己的一份lock文件(yarn.lock),所以如果在项目中混用yarn和npm可能会引起依赖管理的混乱。

  2. 对于npm、cnpm、npx、pnpm和yarn,他们的源都是由npm网站提供的,都指向相同的模块。他们的工作原理基本相同,下载和提取相同的包。

    在大多数情况下,可以确保包的一致性。为了进一步保证一致性,一些管理器(如yarn和pnpm)提供了lock文件,记录了安装的每个包的确切版本,以确保无论在何时何地执行安装,都会得到完全一样的依赖。

    镜像站(如cnpm)提供的包也是与npm保持同步的,但在短时间内可能存在一些延迟。

镜像的使用

(话说总感觉我的梯子对npm比较不友好,其他的github和google服务都比较流畅,npm老卡)

  1. cnpm,其实使用cnpm会更好(比指定镜像源快,可能会自动选择?),但是cnpm不能用于pnpm、yarn等
  2. 配置镜像源
npm get registry                                       # 查询当前源
npm config set registry https://registry.npmmirror.com # 国内
npm config set registry https://registry.npmjs.org/    # 官方

# pnpm和yarn的语法完全一致,只要将`npm`修改成`pnpm`或`yarn`即可
  1. 单次临时使用
npm --registry https://registry.npm.taobao.org install XXX(模块名)

常用的npm镜像源:

  • cnpm的默认源 https://registry.npmmirror.com
  • 淘宝 https://registry.npm.taobao.org
  • 腾讯 http://mirrors.cloud.tencent.com/npm/
  • 如果记不住又不想用cnpm又不记得源,可以先 npm install cnpm,再 cnpm get registry 查 (好别扭)

坑:需要注意!!!哪怕使用 cnpm get registry 的相同源,结果可能也和cnpm不同!如下,cnpm能成功!但 --registry 指定了相同的源却只能下载较低版本的typescript版本!不知道为什么

PS H:\Git\Private\Group_FrontEnd\obsidian-node-flow2> npm --registry https://registry.npmmirror.com/ install -D typescript@^5.0.0
npm ERR! code ETARGET
npm ERR! notarget No matching version found for typescript@5.0.0.
npm ERR! notarget In most cases you or one of your dependencies are requesting
npm ERR! notarget a package version that doesn't exist.

npm ERR! A complete log of this run can be found in: C:\Users\76269\AppData\Local\npm-cache\_logs\2024-10-19T08_40_07_711Z-debug-0.log
PS H:\Git\Private\Group_FrontEnd\obsidian-node-flow2> npm install -D typescript@^5.0.0                                           
npm ERR! code ETARGET
npm ERR! notarget No matching version found for typescript@5.0.0.
npm ERR! notarget In most cases you or one of your dependencies are requesting
npm ERR! notarget a package version that doesn't exist.

npm ERR! A complete log of this run can be found in: C:\Users\76269\AppData\Local\npm-cache\_logs\2024-10-19T08_40_57_066Z-debug-0.log

PS H:\Git\Private\Group_FrontEnd\obsidian-node-flow2> cnpm  install -D typescript@^5.0.0                                         
√ Linked 1 latest versions fallback to H:\Git\Private\Group_FrontEnd\obsidian-node-flow2\node_modules\.store\node_modules
√ Installed 1 packages on H:\Git\Private\Group_FrontEnd\obsidian-node-flow2
√ All packages installed (1 packages installed from npm registry, used 662ms(network 661ms), speed 1.33MB/s, json 1(898.07KB), tarball 0B, manifests cache hit 0, etag hit 0 / miss 1)

devDependencies:
+ typescript ^5.6.3

PS H:\Git\Private\Group_FrontEnd\obsidian-node-flow2> cnpm get registry 
https://registry.npmmirror.com/

结构目录

基本目录

node_modules目录是Node.js包管理器(npm或pnpm)安装的所有依赖包的默认存放位置。其基本的目录结构如下:

多个依赖包:

  • /node_modules/
    • package-1/
    • ...
    • package-n/
    • @scoped-user/
      • package-1/
      • ...
      • package-n/

每个依赖包都有自己的目录,命名为包名。如果包被发布在一个scope下(例如@typescript-eslint/eslint-plugin),那么它会在一个名为scope的目录下。

每个依赖包的目录

每个依赖包的目录下通常包含以下文件和文件夹:

  • package-name/
    • node_modules/, 这个包所需要的其他依赖包
    • package.json, 描述包的元信息(如版本,依赖,入口文件等)
    • index.js or package-main-field.js, 包的入口文件
    • ..., 可选的其他文件

重复/加速管理

在大型项目中,相同的包可能在不同地方多次安装。为了避免重复,npm 和 pnpm 都提供了一种叫做hoisting的机制,可以提升相同的包到更高的node_modules目录下。

同时,pnpm 通过 交叉链接(symlink) 和一个叫做 .pnpm 的全局存储层,来进一步避免重复和加速安装。

知识细节

原理

更多的命令参数

@ 标志

在npm中,有@开头的包通常表示它是一个命名空间包,也就是说它属于一个包的集合。这是npm的scoped packages特性,允许用户将相关的包组织在一起。

例如,@angular/core@angular/common@angular/forms等都是Angular团队发布的包,他们都在@angular这个命名空间下。

相比之下,没有@开头的包是普通包,它们没有特定的命名空间。例如,expresslodashmoment等。

这两种类型的包在功能和使用上没有本质区别,主要的区别在于组织和命名方式。使用命名空间的好处是,它可以避免包名冲突,使得包的组织更加清晰,并且可以更好地表示包之间的关系。

发布到npm仓库 (实例)

参考:https://www.bilibili.com/video/BV1MS4y1L7QW,以Vue封装组件并发布到npm仓库为例

项目准备

vue create my-app
npm run serve
新建 /src/package 文件夹,防止需要上传npm的组建

目录结构:

  • /src/
    • .../
    • package/
      • pig-button/
        • index.vue
      • pig-input/
        • index.vue
    • index.js (需要创建这个来使用Vue install方法)
  • App.vue

组件封装

使用Vue插件模式,会用到Vue提供的一个公开方法:install。这个方法会在使用 Vue.use(plugin) 时被调用。这样使我们的插件注册到全局,在子组件的任何地方都可以使用

index.js

import PigButton from "../package/pig-button/index.vue" // 引入封装好的组件
const coms = [PigButton]; // 将来如果有其他组件,可以写到这个数组里

// 批量组件注册
const install = function (Vue) {
    coms.forEach((com) => {
        Vue.component(com.name, com);
    });
};

export default install; // 这个方法以后再使用的时候就可以被use调用

组件打包

修改 package.json 文件,配置打包命令:

"srcipts": {
    "package": "vue-cli-service build --target lib ./src/package/index.js --name pig-ui --dest pig-ui";
    // --target lib: 打包到的目录
    // --name: 打包后的文件名字
    // --dest: 打包后的文件夹的名称
}

然后打包

npm run package

发布到npm

cd /pig-ui/
# 里面也有一个 package.json,可以去修改里面的东西
# 注册npm账号
npm config set registry=https://registry.npmjs.org # 修改为官方源,避免自己之前改成了淘宝源
npm adduser # 添加 npm 用户
npm publish # 发布 npm

组件使用

可参考element-ui的用法

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'

Vue.use(ElementUI);

父组件

import PigUi form 'pig-ui-test2'
import 'pig-ui-test2/pig-ui.css'

Vue.use(PigUi)

子组件

<template>
	<div id="app">
        <pig-button></pig-button> <!--由于前面全局注册过了,这里直接使用就行了-->
    </div>
</template>

实战技巧:pnpm workspace 管理 monorepo

详见 [../子项目构建系统/pnpm monorepo] 笔记