升级 monorepo 架构
monorepo 是什么
- 定义:单个仓库,组织代码的一种方式
- 把所有相关的项目都放到一个仓库中, 一般情况下放到 packages 中,但不代表只能放在 packages 中
- 本质是把相同的逻辑抽离出去进行复用
优点
- 共用基础设施,不用重新配置
- 有依赖的项目之间,调试开发十分方便
- 第三方的版本管理更简单
缺点
- 项目粒度的权限管理问题
- 代码量比较庞大,对新手不太友好
主流的 monorepo 工具
- npm
- yarn
- pnpm
- larne
- turborepo 让我们更快的执行命令 build,采用并串行,是会影响执行命令的速度的
- nx
- RushJS
1-3 支持简单的 monorepo,复杂的就不够用了
Vue3 是如何做的
-
vue3 从原来的 yarn 管理工具,换成了 pnpm,
配置 workspace, 在 packages 里面放入模块,每个模块都有相应的 package.json,整体有一个 build.js 可以对所有模块进行打包
-
pnpm 节约磁盘空间并提升安装速度
创建非扁平化的 node_modules 文件夹
-
build 命令 script/ build.js
-
test 命令 依赖 build
关于 mini-vue 的 monorepo 的修改
使用 pnpm 的 monorepo
- workspace
- pnpm.io/zh/workspaces
- 命令
- pnpm i xxx -W 安装到 root 目录下
- pnpm i xxx -F xxx 安装到指定目录下 (指明 Pnpm的命令生效于哪个子包)
- pnpm-workspace.yaml 配置,用来指定 packages 路径
js
packages:
-"packages/*"
- 包的依赖顺序
- reactivity 依赖 shared
- runtime-core 依赖 reactivity shared
- runtime-dom 依赖 runtime-core
- compiler-core 依赖 shared
调整 build 逻辑
使用 vitest 替换 jest
- vitest 是未来趋势,也是 vue 系列的一款工具
开始实操
- 创建 workspace 配置
- 创建 pnpm-workspace.yaml 文件
js
packages:
-"packages/*"
- 在对应的模块里面引入对应依赖的模块
-
我们在根目录创建 packages 目录
- 防止路径发生改变,我们直接将 src 里面的文件目录复制到 packages 里面
- 参照 vue3 目录,我们把原来使用 jest 测试的目录 tests 改为 tests
- 我们为每个模块都要创建 package.json
- pnpm init (pnpm 10.24.0 node 22.21.1)
- 在每个模块的 package.json 中 name 属性值改为 @guide-mini-vue/compiler-core (具体的模块名称)
- 注意在 vue 文件目录中的 package.json 的 name 的属性值是 guide-mini-vue
-
我们在文件内部引入文件位置做修改
js// reactivity/src/baseHandlers.ts import { extend, isObject } from "../shared" // 上面的引入路径如何修改 import { extend, isObject } from "@guide-mini-vue/shared"- @guide-mini-vue/shared 这个是让本地包以第三方包的形式导入进来,我们进行处理,有两种方式:
-
第一种:
- v8 及以前的 pnpm 版本
- 第一种 通过 cd 命令,进入 reactivity 目录 pnpm i @guide-mini-vue/shared
- 第二种 进入项目根目录 pnpm i @guide-mini-vue/shared -F @guide-mimi-vue/reactivity
- v9 及以后的 pnpm 版本
-
尝试上面两种方法,发现在下载时直接从 官网下载包去了,而不是从本地下载,从而下载失败, 应该是版本不一样的问题,解决一下
- 在 reactivity 目录的 package.json 中,直接手动写入 shared 包的来源, 然后通过 pnpm install 来下载,发现可以
json"dependencies": { "@guide-mini-vue/shared": "workspace:^1.0.0" }
-
- v8 及以前的 pnpm 版本
-
第二种:
-
或者这个命令也可以下载本地依赖
jspnpm add @guide-mini-vue/shared --filter @guide-mini-vue/reactivity --workspace -
除此之外,我们发现,包的路径还有 ts 的报红,我们进行配置
json"compilerOptions": { "paths": { "@guide-mini-vue/*": [ "./packages/*/src" ] } } -
-
- 如果我们删除已经存在的模块可以使用这样的命令, 这里不用加 --workspace
jspnpm remove @guide-mini-vue/shared --filter @guide-mini-vue/reactivity- 这里的原包与后来下载引入的包之间是软连接,修改其中一方,另一方也会发生变化
- 参照上面的改动方法,我们把这些模块之间的引入路径都使用 @guide-mini-vue/模块名 的方式进行替换
- @guide-mini-vue/shared 这个是让本地包以第三方包的形式导入进来,我们进行处理,有两种方式:
- 优化点
- ts 默认检测目录下所有 ts 后缀文件,我们进行配置,仅检测指定文件
json
// "rootDir": "./src", // 注意这里的 rootDir 如何原来配置了路径,这里可以直接注释
"compilerOptions": {},
"include": ["packages/*/src", "packages/*/__tests__"]
- 总结
- 我们根据目录知道 runtime-dom 依赖 runtime-core , runtime-core 依赖 reactivity ,runtime-dom 不能直接依赖 reactivity
- compiler-core 不依赖其他,仅依赖一个工具库,
- reactivity 为什么可以移动到别的框架里面去使用呢?可以和 Vue 解耦呢?因为它是高层次模块,不依赖于任何低层次的模块,runtime-core runtime-dom 都是和运行时强绑定的,reactivity 可以非常轻松的拿出去给其他模块去使用,同样的 我们在解析 template 为 render 函数时,运行时要用到 compiler-core 的代码,都是先由 compiler-core 导出到 vue 目录,再引入到运行时目录
- 编译流程
- 配置 rollup.config.js
js
// guide-mini-vue/rollup.config.js
import typescript from '@rollup/plugin-typescript'
import pkg from './package.json'
with {
type: 'json'
};
export default {
input: './packages/vue/src/index.ts', // ✅
output: [{
file: './packages/vue/dist/guide-mini-vue.cjs.js', // ✅
format: 'cjs',
sourcemap: true,
},
{
file: './packages/vue/dist/guide-mini-vue.esm.js', // ✅
format: 'es',
sourcemap: true,
},
],
plugins: [
typescript(),
]
}
- 我们运行 pnpm build 进行编译,发现有一些 tests 的目录下面的文件路径会报错,我们对路径进行修改,主要是给路径增加了 /src,我们观察发现,在 packages/vue/dist 里面,经过打包出现了构建好的 js 文件。
- 验证我们打包的 js 文件是否有问题,我们把 example 里面的文件复制到 vue 文件夹下查看效果, 我们将 vue 中的 example 里面的文件的路径改为 dist 发现正常运行
- 下面我们把 jest 换为 vitest
- pnpm i vitest -D -w 或者使用 pnpm add vitest -D --workspace-root
- 使用 vitest 时,需要把 descibe, it, expect 从 vitest 中进行导入
- 也可以使用 vitest.config.js 导入
js
// vitest.config.js
import {
defineConfig
} from 'vitest/config'
export default defineConfig({
test: { // 一些全局 api 自动引入,我们不用进行手动引入
globals: true
}
})
// 使用到 jest api 的文件,我们要引入 vi 进行替换
import { vi } from 'vitest'
// 使用 jest 的位置直接使用 vi 就可以
const onStop = vi.fn()
const runner = effect(()=>{
dummy = foo.num
},{ onStop })
// package.json
"scripts": {
"test": "vitest",
}
- 我们运行 pnpm build ,一些测试已经通过了,一些测试报错了,我们来进行解决
*- vitest 不认识 @guide-mini-vue/shared 等模块,我们进行配置
js
import path from 'path'
import {
defineConfig
} from 'vitest/config'
export default defineConfig({
test: {
globals: true
},
resolve: {
alias: [{
find: /@guide-mini-vue\/(\w*)/,
replacement: path.resolve(__dirname, "packages") + "/$1/src" // $1 替换为 (\w*) 里面的内容
}]
}
})
- 终端命令去掉已存在包
js
pnpm rm -D jest @types/jest babel-jest