Get into the habit of writing together! This is the first day of my participation in the "Nuggets Daily New Plan · April Update Challenge", click to view the details of the event .
Influenced by some classmates ( @dongdong acridine ), I feel that it is necessary to record some of the things I have done, whether it is a good experience to step on the pit, maybe some people seem to be running accounts, but there will always be some people who find it a little useful, or You can give me some advice, I think it will be helpful.
I'm @Laffery , a front-end novice just getting started. If you think this article is helpful to you, please give it a thumbs up and swipe away.
background
Back to the topic, when I was developing my own project recently, as the scale of the project got bigger and bigger, and I wanted to develop an additional library, the original project structure was no longer applicable.
For example, the original project structure is like this
# app
├── lib
│ ├── pkg1
│ │ └── package.json
│ ├── pkg2
│ │ └── package.json
│ └── pkg3
│ └── package.json
└── package.json
复制代码
The main pain point I'm experiencing right now is this
-
Why do I have these packages in my project?
-
These packages are general (for myself) packages that I gradually settled down during the development process, and the main body of the project
app
has dependencies on these packages -
I want to develop them together in this project, because there is a lot of code that can be reused
-
-
Imagine that
pkg1
,pkg2
,pkg3
and the dependencies of our main projectapp
overlap. I believe that the friends who have been tortured by node_modules know that this means- slow download
- Project size is too large
-
Each pkg has a variety of npm scripts, of course, the main ones are
dev
,build
,test
, andlint
the like. If you have such a workflow - after each PR, all packages are lint or tested in CI before they are allowed to be merged, then you will be troubled by the need to execute scripts in each package directory in turn.
What is a monorepo?
简单来说就是把多个项目放在同一个代码仓库里,区别于multirepo(每个项目对应一个代码仓库)。它的核心优势就是便于代码复用和版本管理。
这里不多做介绍,如果有不清楚的小伙伴,可以自行搜索。
pnpm
大家都知道,npm是下载nodejs自带的包管理工具,它当然十分优秀,但是有时候下载龟速,为人诟病。
而我近来一直在使用的yarn,很好的解决了一些下载慢的问题,但是这和这篇文章的主题没有关系了。(其实yarn也能做monorepo,但是为什么我用pnpm呢?因为我在上一家公司见到的就是pnpm)
谈谈这篇文章的主角,pnpm。
节约磁盘空间并提升安装速度
pnpm有一个概念,叫做workspace
,就是将一个仓库分成不同的工作空间,一般是一个package对应一个workspace。
pnpm在执行pnpm install
(根目录下)时,做的事情就是将所有workspace的所有的依赖,下载到node_modules/.pnpm
目录下,然后再根据各workspace的依赖,在其目录下通过软链接的方式将这些依赖添加进来。
盗用一下官方的图
相信看到这里大家能意识到,所有依赖只需要下载一次,那么下载不仅快,而且占用的磁盘体积也小。
对原有项目进行monorepo改造
我们重新调整下项目的结构
# app
├── packages
│ ├── pkg1
│ │ ├── package.json
│ │ └── pnpm-lock.yaml
│ ├── pkg2
│ │ ├── package.json
│ │ └── pnpm-lock.yaml
│ ├── pkg3
│ │ ├── package.json
│ │ └── pnpm-lock.yaml
│ └── app
│ ├── package.json
│ └── pnpm-lock.yaml
├── package.json
├── pnpm-lock.yaml
└── pnpm-workspace.yaml
复制代码
是不是感觉清爽很多[旺柴]。
我们在pnpm-workspace.yaml
中对workspace进行配置,好让pnpm知道都有哪些workspace。
# ./pnpm-workspace.yaml
packages:
# root directory
- "."
# all packages in subdirs of packages/
- "packages/**"
# exclude packages that are inside test/ directories
- "!**/test/**" # '!' means exclude
复制代码
现在动手执行pnpm install
,和前面说的一样,所有设定好的workspace的依赖同时被下载下来了。
执行各workspace的脚本
现在我们回到开头提到的一个场景。
如果你有这样一个workflow--每次PR后在CI里面把所有包都进行lint或test测试之后才允许合并,那么你一定会为需要依次到各个包目录下执行脚本而烦恼。
在根目录下执行app下的脚本
通常你要执行app包下指令,可能会这样做
cd packages/app
npm run xxx
复制代码
或者你是个省事小能手,在./package.json
里面这样写
# ./package.json
{
// ...
"scripts": {
"app:xxx": "cd packages/app && npm run xxx"
}
// ...
}
复制代码
这样你就只需要在根目录下执行npm run app:dev
就可以了。
这种做法未尝不可,但是假如有一天你心血来潮,把这个包的目录名称改成了webapp
,就得忍痛把这些指令全部改成cd packages/webapp && npm run xxx
。
pnpm提供一个--filter
(-F
)指令,你可以设定选择器,让pnpm在特定包下执行指令。
我们假设上面提到的包叫这些名字(package.json里的name)
- pkg1: @laffery/pkg1
- pkg2: @laffery/pkg2
- pkg3: @laffery/pkg3
- app: @laffery/app
现在你要执行app下的dev
指令,只需要
pnpm run --filter @laffery/app dev
复制代码
我们一般很少改包的名字,所以这样改目录名字就不会有问题。
当然了,我们还是一个省事小能手,可以把所有常用的指令都配置在根目录下。
# ./package.json
{
// ...
"scripts": {
"app:dev": "pnpm run --filter @laffery/app dev",
"app:build": "pnpm run --filter @laffery/app build",
"app:deploy": "pnpm run --filter @laffery/app deploy"
}
// ...
}
复制代码
现在我们所有的命令都可以在根目录下执行了。
批量执行命令
这个--filter
既然是过滤器,又不是只选择其中一个,当然是支持筛选多个workspace啦。
现在你想对所有的包进行lint或test,可以这样做
# ./package.json
{
// ...
"scripts": {
"lint": "pnpm run --filter=\"@laffery/*\" lint",
"test": "pnpm run --filter=\"@laffery/*\" test"
}
// ...
}
复制代码
然后在你的.github/workflows/ci.yaml
下轻松地写下
# .github/workflows/ci.yaml
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install
run: npm i -g pnpm && pnpm i
- name: ESlint
run: pnpm lint
- name: Tests
run: pnpm t
复制代码
大功告成!
Of course, there are some other functions, such as unifying the publish npm package, you can check it out on the official website.
Reuse the code under the same repository
Suppose app
depends on [email protected]
and [email protected]
, and the latter both depend on [email protected]
. The normal practice is to use the version on npm directly. But if you want to directly use the one that is currently under development [email protected]
, but it has not been published on npm, it is difficult to do.
Not at all import xxx from "../../pacakge/pkc3/xxx"
.
At this time, the workspace comes in handy, you can pkg1
write dependencies like this
// packages/pkg1/package.json
{
"dependencies": {
"@laffery/pkg3": "workspace:1.5.1",
}
}
复制代码
For a detailed introduction, see the documentation on the official website .
VSCode Workspace
In addition, if you are a VSCode fan like me, you can consider creating a new xxx.code-workspace
file in the root directory
{
"folders": [
{
"path": "packages/pkg1",
"name": "Package 1"
},
// ...
{
"path": "packages/app",
"name": "Web Application"
},
]
}
复制代码
The editor will pop up a 打开工作区
button, click it will have a miraculous effect~, for more information, please refer to .
Summarize
This article introduces some experience in building a monorepo project based on pnpm, you can
- Save disk space and increase installation speed
- Convenient unified management, execute a series of instructions such as testing, building, and publishing
- Reuse the code of other packages under the same repository
This article is simultaneously published on my personal blog, if you are interested, you can click here .
I'm @Laffery , a front-end rookie just hitting the road. If you like my article, or find it useful, please like/follow.