✏️ 接上一篇npm link调试本地npm包的内容,本篇相当于给还上一篇拔草 :)
workspaces介绍
workspaces又称npm工作区 ,官方文档请移步:docs.npmjs.com/cli/v10/usi...
它是npm7才新增的一组功能,其目的也是为了解决本地npm包的调试问题。
为什么说是一组功能,因为workspaces包含多条命令的使用。
workspaces跟npm link不同的是,它是通过 package.json
文件 workspaces
的属性所定义的工作区来进行自动符号链接本地调试包的。
再讲白一点就是,npm link是通过设置类似全局npm包的方式来进行软连接的,而workspaces是通过 package.json
里定义的npm工作区来自动软连接的。
为方便后文理解,先简要科普下软链接和硬链接的概念。
硬链接:
- 硬链接(英语:hard link)是电脑文件系统中的多个文件平等地共享同一个文件存储单元;
- 删除一个文件名字后,还可以用其它名字继续访问该文件;
- 相当于删除硬链接,不会删除所链接的文件;
软链接:
- 软链接(又称符号链接,英文soft link、Symbolic link)是一类特殊的文件;
- 其包含有一条以绝对路径或者相对路径的形式指向其它文件或者目录的引用;
- 相当于删除软链接后,所链接的文件也会随之删除(所以删除要小心)
软链接和硬链接听起来很熟悉,实际用起来我们也不陌生,因为pnpm的主要实现原理就是利用软链接和硬链接来管理npm依赖项。
软链接的文件夹会有特殊标识,如下图所示:
为什么要投奔workspaces?
workspaces是npm官方新增的功能,他不仅可以代替npm link原有的功能,还多了以下优点:
1. 不仅支持单个npm包调试,还支持多个包调试
bash
// package.json
{
"name": "my-workspaces-project",
"workspaces": ["packages/a", "packages/b"]
}
该配置同时指定了 packages/a
和 packages/b
两个工作区,所以 a/ 和 b/ 两个目录可以放置两个不同的npm包来进行本地调试。
workspaces 接受一个字符串数组,将你本地要调试的多个npm包目录配置进去即可。
2. 简化了本地npm包调试的工作流程
一次配置,一劳永逸。这种可编程的配置化方式比在控制台npm link、npm unlink敲命令更方便,更显优雅和规范。
workspaces通过指定npm工作区的方式,省去了我们每次调试都要先在npm包根目录执行一次npm link
,再在npm包调试项目目录执行一次npm link [包名]
的冗余过程,还很好的避免了发包以后忘记npm unlink的麻烦,为开发者极大的减轻了不必要的心智负担。
更何况npm link使用起来坑不少,稍不留神就很容易遇到问题,请参考另一篇分享:本地调试npm包之npm link。
Less is better,还是应该将时间花费在那些美好的事物上,而不是浪费在填坑上。
3. 代码可配置化,更方便管理调试包的依赖关系
npm link关联软链接和解除关联的方式均是通过代码配置,从package.json的配置就可以清晰看到正在调试的是哪些包。
这种通过设定npm工作区的方式来调试本地npm包更直观,也更方便使用。
4. 调试过程中,改动调试包会自动热更新
一旦指定npm工作区,你要调试的npm包就被作为依赖共享了。所以当你改动要调试的npm包时,依赖方可以自动更新到最新代码,非常的智能。
而npm link的方式,每次更新完你要调试的包,你都需要重新在你调试包的目录执行一次npm link。虽然只是切换个目录,再敲一句命令的事,但是频繁改动时这个动作显得尤为重复,对新手来讲也很容易遗漏执行造成迷惑。
有人觉得多敲几下没啥,但我还是觉得:程序员最应该坚守的原则之一,就是不应该理所当然的把重复当作常态。
5. 可实现包依赖共享,优化项目体积
由于使用workspaces安装的所有子应用的依赖包,都会被提升到根目录中进行统一安装,可以让各个子应用相互共享npm依赖包,避免很多npm的重复安装,从而优化了项目的总体积。
整个过程是自动的,只需要你设定好工作区,你就算直接在工作区安装自己的npm包,它也会自动被加到根目录的node_modules中。
Workspaces如何使用?
Talk is cheap,show me the code. 说再多都不如亲自动手试一试。
如何定义新工作区?
假设我们项目根目录是这样:
bash
├── packages
| ├── a
├── package.json
当我们在根目录执行 npm init -w ./packages/a -y
后,/packages/a 就变成了我们项目的一个工作区。
如果要一次性设定多个工作区,可以执行 npm init -w packages/a -w packages/b -y
。
此时项目目录如下:
bash
├── packages
| ├── a
| | ├── package.json
| ├── b
| | ├── package.json
├── package.json
package.json里你会发现,此时也多了一条配置:"workspaces": ["packages/a", "packages/b"]
你在/packages/a、/packages/b开发好你的代码后,只需在根目录执行一下 npm install
就会将a、b两个目录作为npm的依赖包(实际上只是一个软链接)放置在根目录的node_modules中。
需要注意两点:
- 不需要你在项目根目录package.json依赖里加上工作区a和b
- 一般我们要支持 ESM 方式,所以我们需要将packages里的所有 package.json 都新增一个字段:
"type": "module",
然后再npm install
如果原来/packages/a里已经有你开发好的代码和package.json(有node_modules则需要先删除),你可以定义好workspaces
字段,直接在根目录npm install
也可以成功定义好npm工作区。
如何删除工作区?
例如你想删除"packages/a"
这个工作区,你只需要将根目录package.json改成:"workspaces": ["packages/b"]
,最后再重新 npm install
即可。
如何优雅的管理工作区依赖?
a. 安装工作区依赖
给工作区packages/a
安装dayjs依赖:
npm i dayjs -w a
如果要同时给所有工作区安装dayjs依赖:
npm i dayjs -ws
b. 移除工作区依赖
给工作区packages/a
卸载dayjs依赖:
npm uninstall dayjs -w a
当然,你也可以直接切换到你工作区目录去执行install或者uninstall命令,效果与之等价。
在工作区中运行命令
例如要执行工作区a的一些命令,如项目启动(dev)、单测(test)等,可以通过以下命令:
npm run dev -w a
或者npm run test-w a
单独部署某个工作区代码
如果只想部署a工作区的代码:
bash
npm install --production --workspace=a
npm run prod --workspace=a
借助workspace发布工作区的npm包
比如你项目的a工作区是一个npm包,已经支持npm run build命令,且已经npm install过。
当a工作区要发包,一般常规的流程是:切到a工作区目录-》 npm run build -》npm publish。
我们可以在根目录配置这两条命令:
json
// package.json
{
"scripts": {
// ...
"build": "npm run build --workspace=a",
"publish": "npm publish --workspace=a",
}
}
这样我们就可以在根目录就直接执行 npm run build
、npm run publish
来给一个工作区发包了。
Workspaces的用武之地
脱离应用场景谈技术好坏,纯属是耍流氓
UI 组件库实现按需导出
当遇到需要开发支持按需导出的UI组件库项目时,我们各个UI组件需要支持单个导出,所以需要在一个项目里支持多个UI组件各自独立调试,互不干扰。
这种场景下最好的方式就是使用workspaces方案了,我们只需在UI组件库项目根目录的package.json中,将所有UI组件目录都添加到workspaces
的数组中即可。
配合 Monorepo 食用更佳
当一个大项目代码越来越多,维护起来越来越困难时,就需要将该项目拆成几个耦合度较低的小应用。这种一个大应用由几个小应用组成的项目,就是一个 Monorepo 架构。
前端很多框架/库的源码都是monorepo架构,比如Vue、React等等。
Monorepo 项目的代码组织方式大致如下:
bash
├── packages
| ├── pkg1
| | ├── package.json
| ├── pkg2
| | ├── package.json
├── package.json
这种场景似乎完全就是为workspaces量身定做的,使用workspaces方案来相互共享各个子应用的npm依赖包,可以实现npm依赖包的重复利用。
接着上述项目代码的案例,我们通过workspaces来安装pkg1、pkg2的依赖项,会将pkg1、pkg2的node_modules都合并在一起,然后统一放到根目录。既方便资源共享,也方便维护和管理。
写在最后
workspaces是npm里很容易让人忽视的功能,因为日常业务开发不太用得上,但它却是npm进阶不得不掌握的内容。
它既能代替npm link更方便的调试本地npm包,还有很多npm link不具备的功能,堪称前端工程化的利器。
计划后续在建设移动端UI组件库的过程中,继续进行workspaces的深度实践和探究。