外观
简介
本文只是介绍关于 pnpm 的 workspace 和 git Submodule 的基本用法,作者水平有限,可能比较简陋,也或许有些错误,欢迎指正。 官方入口 主要作用:
- 统一管理多包依赖:通过一个根 pnpm-workspace.yaml 文件定义所有子包,pnpm 可以在工作空间内共享和优化依赖。 不同包中相同的依赖只安装一次,避免重复安装,提高空间和安装速度的效率
- 本地包的互相依赖:在工作空间内,子包可以直接通过 workspace: 引用彼此,而不需要发布到 npm。
- 支持批量操作:作空间可以一次性对多个子包执行相同的命令,如安装依赖、运行脚本等
- 跨包依赖版本一致性:通过根 package.json 定义通用依赖,所有子包可共享这部分配置,确保依赖版本一致。
创建一个 pnpm 的 Workspace
不多扯了,直接动手!!!
1、创建一个文件夹,并且初始化 pnpm
workspace 初始化
bash
mkdir workspace
cd workspace
pnpm init
2、创建 pnpm-workspace.yaml 文件
在workspace根目录创建,此文件会告诉 PNPM,哪些子目录应被视为工作空间的一部分
yaml
packages:
- 'packages/*' // 这里可以单独指定子包,也可以使用通配符,如 packages/*
3、创建子包(这里演示用,一个公共方法,一个公共组件)
首先创建一个公共方法项目:
- 在packages下新建一个文件夹:public-method
- 然后在 public-method 下执行 pnpm init
- 在 public-method 下新建一个 src 文件夹,然后创建一个 index.ts 文件,内容如下:typescript
export const randomStr = (str : string) : string => { return str + Math.random() }
- 修改 public-method 下的 package.json 文件,主要设置 name 和 main 两个字段 如下:json
{ "name": "@public/method", "version": "1.0.0", "description": "", "main": "src/index.ts", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }
目录展示:
workspace/
├── package.json # 根 package.json
├── pnpm-workspace.yaml # 工作空间配置
├── packages/
│ ├── public-method/
│ │ ├── package.json
│ │ └── src/
│ │ └── index.ts
公共组件包创建步骤同上,但是内容略有不同, 目录如下:
workspace/
├── package.json # 根 package.json
├── pnpm-workspace.yaml # 工作空间配置
├── packages/
│ ├── public-method/
│ │ ├── package.json
│ │ └── src/
│ │ └── index.ts
│ ├── public-ui/
│ │ ├── package.json
│ │ └── src/
│ │ └── components
│ │ └── index.ts
│ │ └── input
│ │ └── index.ts
│ │ └── input.vue
│ │ └── style
│ │ └── components
│ │ └── input.scss
│ │ └── index.scss
│ │ └── index.scss
│ │ └── index.ts
一些主要文件: src/index.ts
typescript
import './style/index.scss'
export * from './components'
import * as components from './components';
const MyUIObj = {
...components
}
export const install = function(app) {
Object.keys(MyUIObj).forEach(key => {
app.component(key, MyUIObj[key]);
// todo i-tag
});
}
const MyUI = { install, ...components }
export default MyUI
src/style/index.scss
@use './components/index.scss';
src/style/components/index.scss
@use "./input.scss";
src/style/components/input.scss
.my-input {
border: 1px solid blue;
}
src/components/index.ts
export { default as Input } from './input'
src/components/input/index.ts
import Input from './input.vue'
export default Input
src/components/input/input.vue
<template>
<input class="my-input" type="text">
</template>
<script lang="ts" setup>
</script>
4、安装依赖(公共依赖、单独依赖)
下面先安装一下所有的包都有或者大部分的包都需要用到的依赖项, 修改 workspace的 package.json ,增加如下内容:
json
"dependencies": {
"pinia": "^2.2.6",
"sass": "^1.81.0",
"vue": "^3.5.12",
"vue-router": "^4.4.5"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.1.4",
"@vitejs/plugin-vue-jsx": "^4.0.1",
"vite": "^5.4.10",
"vite-plugin-vue-devtools": "^7.5.4"
}
直接在 workspace 目录下执行 pnpm install 现在在 workspace 目录下,所有子包都安装好了,并且依赖版本也一致了。 接下来引入我们刚刚创建的两个公共包,依次在 workspace 目录下 执行命令 :
pnpm install @public/method --workspace -w
pnpm install @public/ui --workspace -w
就可以看到 package.json 中增加了两个依赖:
json
"dependencies": {
"@public/method": "workspace:^",
"@public/ui": "workspace:^",
"pinia": "^2.2.6",
"sass": "^1.81.0",
"vue": "^3.5.12",
"vue-router": "^4.4.5"
},
这样我们基本框架搭建起来了,下一步就是,创建一个简单的项目,来测试一下是否能够成功引用到公共包。
5、创建两个简单的项目,来测试一下是否能够成功引用到公共包
诶嘿,变成两个了!! 直接在 packages 目录 运行 命令: pnpm create vite
搞到 名为:vue-one 的一个普普通通的vue项目,然后再同样搞一个名为:vue-two的项目 这样我们就有了两个 vue 项目,分别在 packages/vue-one 和 packages/vue-two 中。 修改一下两个项目的 package.json 文件,如下内容:
json
{
"name": "vue-two",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc -b && vite build",
"preview": "vite preview"
}
}
这里注意一下 pnpm-workspace.yaml 文件的 packages 字段下是否添加了 vue-one 和 vue-two。 然后我们在 workspace 目录下执行命令:pnpm install
pnpm 会自动安装所有子包的依赖,并在需要时通过符号链接关联本地包。
现在,可以不管三七二十一,直接 在将 vue-one 和 vue-two 正常启动,没啥问题(诶嘿,我正常启动了,其他的我不到啊😄 )。
下一步,我们在vue-one中调用我们刚刚(谁知道是多久前的)创建的公共包 在 vue-one 的 app.vue 中添加如下代码:
vue
<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue'
import {onMounted, ref} from "vue";
import { randomStr } from "@public/method"
import { Input as MyInput } from '@public/ui'
const str = ref('hello')
onMounted(() => {
let s = randomStr(str.value)
console.log(s);
})
</script>
<template>
<my-input></my-input>
<div>
<a href="https://vite.dev" target="_blank">
<img src="/vite.svg" class="logo" alt="Vite logo" />
</a>
<a href="https://vuejs.org/" target="_blank">
<img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
</a>
</div>
<HelloWorld msg="Vite + Vue" />
</template>
可能现在已经不记得 我们公共包定义的方法和组件是什么了,randomStr 方法 是一个参数是一个字符串,返回的是一个字符串拼接随机数; MyInput 是一个输入框组件,有醒目的蓝色边框。
👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏
vue-two 随便装点依赖,然后测试一下单独引入依赖项是否正常。比如装一个鲁大师,然后 log 一下看看否能成功引入。
6、怎么打包
将就直接在 vue-one 下执行 打包命令,如果vue-tsc 报错,就把打包命令中的 vue-tsc -b && 删除,从根本上解决问题(其实就是没装依赖) 至于打包后能不能用,我先去试一试,稍等两行文字 测试了,可以用
workspace 小结
这就是 pnpm 的 workspace 的基本使用方法,还有很多不同的用法,比如如何使用 pnpm link 链接本地包,如何使用 pnpm patch-package 解决依赖包版本冲突等问题,这里就不一一介绍了,感兴趣的可以自行探索。