哈喽,大家好!我是你们的老朋友,今天咱们来聊聊一个让前端开发效率起飞的话题:Vue 3 + Vite + Monorepo,这仨凑一块儿,绝对能擦出不一样的火花。
开场白:为什么是它们?
- Vue 3: 新一代Vue,性能更好,Composition API更灵活,类型推导更棒。
- Vite: 快,真快!基于浏览器原生ESM,开发体验爽到飞起,再也不用忍受Webpack那漫长的冷启动了。
- Monorepo: 项目结构更清晰,代码复用更容易,依赖管理更优雅,简直是大型项目的福音。
这三者结合,解决的就是大型项目开发中的效率问题、代码复用问题和项目管理问题。
第一部分:Monorepo基础概念与实践
-
什么是Monorepo?
简单来说,Monorepo就是把多个项目或模块放在同一个代码仓库里管理。与之对应的是Multi-repo,每个项目一个仓库。
特性 Monorepo Multi-repo 代码组织 所有项目在一个仓库 每个项目一个仓库 依赖管理 统一管理,方便共享和复用 各自管理,重复依赖可能存在 构建部署 统一构建流程,方便整体发布 各自构建流程,独立发布 代码复用 模块共享更简单,减少重复代码 模块共享需要额外工具或流程 -
Monorepo的优势
- 代码复用: 组件库、工具函数等可以轻松在不同项目间共享。
- 依赖管理: 统一管理依赖版本,避免版本冲突和重复安装。
- 原子性变更: 修改一个底层库,可以一次性更新所有依赖它的项目。
- 协作效率: 团队成员更容易了解整个项目的结构和依赖关系。
-
Monorepo的挑战
- 仓库体积: 整个仓库会比较大,需要考虑Git的性能。
- 构建复杂性: 需要工具来管理项目间的依赖关系和构建顺序。
- 权限管理: 需要更精细的权限控制,防止误操作。
-
Monorepo工具选择
市面上有很多Monorepo工具,比如Lerna、Yarn Workspaces、pnpm Workspaces、Nx等等。这里我们选择pnpm Workspaces,因为它速度快、磁盘占用小、支持幽灵依赖管理,非常适合Vue 3 + Vite的项目。
第二部分:使用pnpm Workspaces搭建Monorepo项目
-
初始化项目
首先,创建一个空目录,作为Monorepo的根目录:
mkdir vue3-vite-monorepo cd vue3-vite-monorepo
然后,初始化pnpm:
pnpm init
这会生成一个
package.json
文件。 -
配置
pnpm-workspace.yaml
在根目录下创建一个
pnpm-workspace.yaml
文件,用来告诉pnpm哪些目录是workspace:packages: - 'packages/*' - 'apps/*'
这里我们约定:
packages/
目录存放共享的npm包。apps/
目录存放独立的Vue应用。
-
创建共享模块 (
packages/
)在
packages/
目录下创建一个名为ui-library
的目录,作为我们的组件库:mkdir -p packages/ui-library cd packages/ui-library pnpm init
在
packages/ui-library/package.json
中添加一些基本信息:{ "name": "@vue3-vite-monorepo/ui-library", "version": "0.1.0", "description": "A Vue 3 component library", "main": "dist/ui-library.js", "module": "dist/ui-library.es.js", "types": "dist/ui-library.d.ts", "files": [ "dist" ], "scripts": { "build": "vite build", "dev": "vite" }, "devDependencies": { "vue": "^3.0.0", "vite": "^4.0.0", "@vitejs/plugin-vue": "^4.0.0", "typescript": "^4.0.0", "@vue/compiler-sfc": "^3.0.0", "vue-tsc": "^1.0.0" }, "peerDependencies": { "vue": "^3.0.0" } }
name
: 一定要带scope,例如@vue3-vite-monorepo/ui-library
,这样可以避免和其他npm包冲突。main
,module
,types
: 指定构建后的文件路径。peerDependencies
: 声明对vue
的依赖,避免重复安装。devDependencies
: 开发依赖,包含vite
,vue
,@vitejs/plugin-vue
等。
安装依赖:
pnpm install
创建一个简单的组件:
packages/ui-library/src/components/MyButton.vue
<template> <button class="my-button">{{ label }}</button> </template> <script setup lang="ts"> import { defineProps } from 'vue'; defineProps({ label: { type: String, required: true, default: 'Click Me' } }); </script> <style scoped> .my-button { background-color: #4CAF50; border: none; color: white; padding: 10px 20px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; cursor: pointer; } </style>
创建一个入口文件:
packages/ui-library/src/index.ts
import MyButton from './components/MyButton.vue'; export { MyButton };
配置
vite.config.ts
:packages/ui-library/vite.config.ts
import { defineConfig } from 'vite'; import vue from '@vitejs/plugin-vue'; import { resolve } from 'path'; export default defineConfig({ plugins: [vue()], build: { lib: { entry: resolve(__dirname, 'src/index.ts'), name: 'UiLibrary', fileName: (format) => `ui-library.${format}.js`, }, rollupOptions: { external: ['vue'], output: { globals: { vue: 'Vue', }, }, }, }, });
lib
: 配置库模式构建。entry
: 指定入口文件。name
: 指定库的名称,会在全局暴露。fileName
: 指定输出文件名。rollupOptions.external
: 指定外部依赖,这里我们指定vue
,避免重复打包。rollupOptions.output.globals
: 指定全局变量,这里我们指定vue
的全局变量为Vue
。
最后,构建组件库:
pnpm build
这会在
packages/ui-library/dist/
目录下生成构建后的文件。 -
创建Vue应用 (
apps/
)在
apps/
目录下创建一个名为my-app
的目录:mkdir -p apps/my-app cd apps/my-app
使用Vite初始化Vue项目:
pnpm create vite . --template vue-ts
这会创建一个标准的Vue + TypeScript项目。
安装依赖:
pnpm install
-
在Vue应用中使用组件库
现在,我们需要在
my-app
中使用ui-library
。 由于pnpm Workspaces的特性,我们可以直接通过@vue3-vite-monorepo/ui-library
来引用,而无需发布到npm仓库。修改
apps/my-app/src/App.vue
:<template> <img alt="Vue logo" src="./assets/logo.png" /> <HelloWorld msg="Hello Vue 3 + TypeScript + Vite" /> <MyButton label="Monorepo Button" /> </template> <script setup lang="ts"> import HelloWorld from './components/HelloWorld.vue' import { MyButton } from '@vue3-vite-monorepo/ui-library' // 引入组件库 </script> <style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
启动Vue应用:
pnpm dev
你应该能看到
MyButton
组件成功显示在页面上。
第三部分:Monorepo高级技巧与最佳实践
-
依赖提升 (Hoisting)
pnpm会自动将公共依赖提升到根目录,减少重复安装,节省磁盘空间。 例如,如果
ui-library
和my-app
都依赖lodash
,那么lodash
只会安装一份在根目录的node_modules/
下。 -
依赖隔离 (Isolation)
pnpm使用
node_modules/.pnpm
目录来存储依赖,实现了真正的依赖隔离,避免了幽灵依赖问题。 每个项目只能访问自己声明的依赖,不能访问未声明的依赖。 -
使用ESLint和Prettier进行代码规范
在Monorepo中,保持代码风格一致非常重要。 可以在根目录下配置ESLint和Prettier,并使用pnpm的
filter
功能,只对修改过的项目进行代码检查。// 在根目录的package.json中 { "scripts": { "lint": "eslint --ext .js,.ts,.vue .", "format": "prettier --write ." } }
# 只对apps/my-app目录进行eslint检查 pnpm lint --filter my-app
-
使用Changesets进行版本管理
Changesets是一个非常优秀的Monorepo版本管理工具,它可以自动生成changelog,自动更新依赖版本,自动发布npm包。
首先,安装Changesets:
pnpm add @changesets/cli -D -w
初始化Changesets:
pnpm changeset init
这会在根目录下创建一个
.changeset
目录。当需要发布新版本时,运行:
pnpm changeset
这会提示你选择需要更新的项目,并填写更新说明。 Changesets会将这些信息保存到
.changeset
目录下的markdown文件中。然后,运行:
pnpm version
Changesets会根据
.changeset
目录下的markdown文件,自动更新每个项目的package.json
中的版本号,并生成changelog。最后,运行:
pnpm publish
Changesets会自动发布更新后的npm包。
-
持续集成 (CI/CD)
Monorepo的CI/CD流程需要特别注意。 可以使用GitHub Actions或其他CI/CD工具,根据代码变更情况,只构建和部署受影响的项目。
例如,可以使用
nx affected
命令来找出受影响的项目:nx affected --target=build --base=main --head=HEAD
这会列出所有需要构建的项目。
第四部分:Vue 3 + Vite + Monorepo的优势总结
优势 | 描述 |
---|---|
代码复用与共享 | 组件库、工具函数等可以轻松在不同项目间共享,避免重复代码,提高开发效率。 |
依赖管理 | 统一管理依赖版本,避免版本冲突和重复安装,节省磁盘空间。 |
构建速度 | Vite的快速冷启动和热更新,大大提升了开发体验。即使在大型Monorepo项目中,也能保持流畅的开发体验。 |
项目结构清晰 | Monorepo的结构更清晰,更容易了解整个项目的结构和依赖关系,方便团队协作。 |
版本管理 | Changesets等工具可以方便地进行版本管理,自动生成changelog,自动更新依赖版本,自动发布npm包。 |
CI/CD | 可以灵活地配置CI/CD流程,根据代码变更情况,只构建和部署受影响的项目,提高构建和部署效率。 |
总结与展望
Vue 3 + Vite + Monorepo是一个强大的组合,可以显著提升大型前端项目的开发效率和可维护性。 虽然配置和管理Monorepo需要一定的学习成本,但带来的好处远大于付出的成本。
当然,Monorepo并不是银弹,需要根据项目的实际情况选择合适的工具和架构。 希望今天的分享能帮助大家更好地理解和应用Vue 3 + Vite + Monorepo,打造更高效、更优雅的前端项目。
祝大家编码愉快!