Appearance
npm 下载包流程
- 检查项目中是否存在package-lock.json 文件,如果有,比较和package.json和lock.json(一般都不一致),如果版本兼容则使用lock.json 定义的依赖,如果不兼容使用package.json 并更新。
- 检查缓存,无缓存使用,registry中获取包的tgz压缩包,下载(npm-registry-fetch)到本地缓存目录。
- 从缓存目录解压到node_modules目录,tgz是一个package目录,解压时未包含package目录
npm 本地添加离线包
公司处于内网环境无法下载包,需要自行添加包。
- 使用npm-pack-all 制作tgz离线包。
- 上传离线包到npm仓库
- 网络下载或者使用npm i xxx.tgz安装
yarn 常用命令
npm i => yarn npm i xx => yarn add xx -S npm i xx -S => yarn add xx npm i xx -D => yarn add xx -D npm i -g xx => yarn global add xx npm uninstall xx => yarn remove xx
mutilRepo 和 monoRepo
当团队项目变多,而且各个项目存在关联时,就需要采用monoRepo方式管理。传统的muti的lRepo将项目分散到各个仓库。会出现如下痛点
- 不能批量操作,操作某个项目需要到相应的目录下操作。项目多起来简直就是地狱
- 比如一个组件库升级了版本,需要在组件库修改版本号,发布npm。然后业务库再更新package.json 重新安装依赖。
- 组件库如果是break change 的更新,业务库可能会忘记及时更新
monoRepo出现会解决这些痛点,可以在一个地方对所有仓库进行,仓库相互之间能互相感知。
npm 痛点
npm1 npm2项目下根据项目的package.json下载依赖到node_module,如果依赖存在依赖其他的情况会在node_modules/xxx/node_modules, 而且递归下去。 出现问题是大量重复的包被安装,而且文件目录过生对于查找耗时严重。
npm3 后优化了结构扁平化处理,如果一个依赖没在项目node_modules 出现,依赖的依赖就会安装在项目的node_modules 中,如果两个依赖同时依赖一个版本不一致的库,先安装的依赖在项目下,后安装按照npm1 npm2 处理。这样做也有问题 1.扁平的算法耗时严重。2. 项目未在package.json 中声明的依赖也可以直接引用,会出现不确定性(往往是自动补全代码带来的)。 3. 对于相同依赖的不同版本,完全是按照安装先后顺序来的,会有不确定的情况,这种会对缓存有较大影响(后续的package-lock.json专门解决这个)
pnpm 解决的痛点
pnpm通过软链接的方式把扁平的结构放到.pnpm目录中,同时.pnpm 通过npm2 的方式保证了包的正确性,子package中的node_module也是引用的root node_module/.pnpm的包。
pnpm
常用的命令
pnpm help xxx 查询各cli使用
pnpm config set xx=xxx --location=project | user | global
pnpm xxx 自动查询找package中定义的脚本,如果未查询到当做shell命令执行,pnpm pwd => /xx/xx/xx
pnpm install,安装整个项目包括子项目的依赖, --frozen-lockfile 保证安装依赖不会更新lockFile。-P 只安装Dependencies依赖, -D 只下载devDependencies依赖。
pnpm add xx 安装某个依赖,默认会添加依赖到dependencies, -E 明确版本(必备) -D 保存到devDependencies -P 添加到peerDependencies -O 添加到可选依赖 -W 安装到项目根目录
当运行安装依赖时默认会在子集中查找是否有满足条件的包,如果有引用子项目的,通过--workspace 指定包只能在子项目中查找。npm update 更新某个包,也用于更新子项目。
pnpm env use 16 -g 代替nvm功能安装包,也支持在项目的npmrc 文件中配置use-node-version=16 保证不同的项目使用不同盖尔nodejs
pnpm publish -F @xunserver/test 发布某个子项目,发布项目时,会替换package中workspace协议的依赖。--public=access 发布公共项目 --dry-run 仅执行发布流程但是不发布 --no-git-checks 不检查git --public-branch 设置发布的分支 通过packagejson中的publishConfig字段可以定义在发布时替换相应的package字段
pnpm pack 将需要publish的包xxx.gz输出到本地目录
npm_config
pnpm 复用.npmrc 配置文件,大部分配置都能在cli指定通过选项指定,部分不能指定的配置在.npmrc中key=value,如果需要在cli动态指定参数有两种方案添加npm_config_key=value 的环境变量,二是添加选项--config.key=value 常用的npm_config 有如下
- registry=http://xxxx/ 制定依赖安装的仓库,支持@bable:registry=xxx,指定某个包(或者组织)只按照某个仓库安装
\<URL\>:_authToken url 是仓库的地址,_authToken 支持从环境变量获得${xxx}\<URL\>:tokenHelper tokenHelper指向一个可以执行的文件,通过文件返回的结果作为token。- use-node-version=16.1.1 指定项目运行需要的nodejs版本
- node-mirror:<releaseDir> 默认值 https://nodejs.org/download/\<releaseDir>/ 指定pnpm env use 的下载的nodejs目录
packageJSON需要关注的字段
- engines 指定项目的nodejs版本和pnpm版本
json
{
"engines": {
"node": ">=10",
"pnpm": ">=3"
}
}- publishConfig 发布时替换packageJSON中字段
pnpm 解决patch问题
有时候用的第三方依赖有bug,需要我们自行调整,使用package alias简单实现,下载第三方源码(比如xxx1),修改编译发布到自己的仓库(取了了新名字xxx2)。pnpm add xxx1@npm:xxx2 即可把对xxx1的引用全部换成xxx2。
pnpm 命令补全
pnpm install-completion
用好pnpm的关键 --filter -F
pnpm生产实战
假设项目中存在packageA和packageB,其中packageA依赖于packageB。面临着如下的问题
- packageA在构建生产环境时,使用本地构建的packageB还是发布到环境的packageB
- 如果是CI在构建,是否需要在CI环境上构建packageB
- 本地构建开发环境时,如何引入packageB的包,引入的是构建前的包还是构建后的包
- 本地开发环境如何对packageB的代码进行调试
- packageA需要依赖packageB的老版本,但是当前项目中packageB是新版本,如何解决
packageJson中依赖通过workspace: x.x.x 指定,如果当前项目需要workspace中的版本,则通过这种方式指定,如果当前项目依赖的是老版本,则通过删除workspace的指定
pnpm -F 支持依赖的层级的选择,pnpm -F @xunserver/test... run build。会依次的查找@xunserver/test 依赖的同时存在项目中的包执行build,比如@packageA定义了依赖packageB, npm -F @packageA... run build 会先执行packageB的build,在执行packageA的build
问题一:解决如下,如果依赖的packageB已经发布,同时项目中的packageB比较新,packageA依赖的旧版本的packageB,则应该修改packageA的package删除workspace协议。如果需要使用的packageB是本地最新的,则按照workspace的方式使用,如果使用的是打包后的B,直接在本地执行B的打包
问题二:解决方案同问题一,主要看是否需要使用workspace中的代码,如果需要使用workspace的代码,使用依赖层层构建的方式
问题三 :这个情况有两种场景,一般来说引入packageB不会引入B的源码,都是引入的构建后的代码,因为packageB和packageA不一定使用的相同的构建工具和流程,但是也有packageB的是packageA的子项目,这个时候也就不存在packageB源码的生产代码有区别,直接引用即可, 如果packageB引入的是构建后代码,那么调试的时候就没法找到是哪个文件出错,需要引入sourcemap的调试方法,同时在vscode中源码映射的方式来实现调试。还有个问题需要解决的是,packageB每次调试改动后如何通知packageA重新构建,将watch选项添加到生产环境的构建流程中,新增production:watch的构建选项
问题四:本地开发时如何对packageA的依赖项packageB进行调试,packageB在构建时始终构建sourcemap,特别是在本地开发环境中。针对线上环境,通过调整packageA构建的参数来屏蔽packageB的sourcemap是否引入。packageB构建参数sourcemap后,参考前面的devtools通关秘籍进行调试
问题五:前面已经论述
pnpm 发布流程控制
只允许pnpm
json
{
"scripts": {
"preinstall": "npx only-allow pnpm"
}
}n 包管理器是啥
ni xx -S => npm i xxx -S nr => npm run nrm => npm uninstall nx => npm execute
n 会自动检查项目下存在package-lock.json、yarn.lock或者pnpm-lock.yaml 自动选择包管理器安装。
.npmrc、.yarnrc和.pnpmrc
npmrc格式是key=value的形式 value和key有空格需要用""包起来 yarnrc格式是key value 的形式,有空格需要用""包裹 pnpmrc使用的npmrc配置
packageJson中需要关注的字段
dependencies: 生产依赖,总是会被安装 dev dependencies: 如果这个包出现在首应用中,总是会被安装,如果是作为另一个包的依赖,则不会安装,比如A依赖了B,B中定义dev dependencies的不会被安装 peer dependencies:声明这个包不是我需要直接安装的,我只是需要这个包,要求依赖方自行安装依赖。npm7后也会自动安装。安装过程如果碰到重复依赖不能处理的情况会报错,添加--legacy-peer-dep 会忽略冲突的依赖,以先安装的为准
