Vue.js 生态系统深度探索与项目实战:一个佛系程序员的摸鱼学习笔记
早上8点,北京地铁10号线又挤得像沙丁鱼罐头。我一边单手抓着扶手,一边用另一只手刷GitHub Trending——是的,这就是我这个“躺平但技术不能停”的程序员日常。通勤一小时,不看点代码总觉得亏了,毕竟工资不是白拿的(虽然我总觉得自己在摸鱼)。
最近公司有个新项目要上,是个内部管理后台,技术栈指定Vue 3 + TypeScript。说实话,之前一直在用Vue 2,对Composition API只是“听说过”,连ref和reactive的区别都快记混了。产品经理上周五下班前突然甩来一句:“下周三要能跑起来,不然双11大促的数据看板就挂了。”当时我差点把咖啡喷到显示器上——这哪是需求,这是催命符啊!
于是,周末两天没敢躺平,硬着头皮把Vue生态翻了个底朝天。今天这篇笔记,就是我在“被deadline追着跑”的过程中,边踩坑边总结出来的实战心得。如果你也正被Vue 3的生态搞得头晕眼花,或者想从Vue 2平滑过渡,那咱俩可以一起摸会儿鱼,顺便学点东西。
为什么Vue生态越来越“重”了?
记得刚入行那会儿,一个<script src="vue.js">就能开干。现在?光是搭个环境就得装十几二十个包:Vite、Pinia、Vue Router、ESLint、Prettier、TypeScript……更别提各种UI库(Element Plus、Naive UI、Ant Design Vue)选哪个都能吵半天。
说真的,Vue生态变“重”,不是因为框架本身复杂了,而是我们对工程化、可维护性、团队协作的要求高了。
举个例子:以前一个小功能改完直接上线,现在得过CI/CD流水线、单元测试、E2E测试、代码审查……运维大哥还要求首屏加载不能超过1.5秒。所以,光会写模板已经不够了,你得懂整个工具链怎么串起来。
项目初始化:别再用vue-cli了,试试Vite!
我一开始习惯性地敲了vue create my-project,结果发现官方文档首页赫然写着:“推荐使用 Vite”。好吧,时代变了。
Vite 的优势在哪?快!快!快!
开发服务器启动从原来的8秒降到0.3秒,HMR(热更新)几乎是瞬时完成。这对于我这种频繁改一行代码就要刷新十次的人来说,简直是救命稻草。
初始化命令很简单:
npm create vue@latest my-vue-app
这个命令会引导你选择是否启用TypeScript、Pinia、Vue Router、ESLint等。我全勾了——反正以后都要用,不如一步到位。
生成的项目结构很清爽:
my-vue-app/
├── src/
│ ├── assets/
│ ├── components/
│ ├── views/
│ ├── App.vue
│ └── main.ts
├── public/
├── vite.config.ts
└── package.json
重点说下 vite.config.ts,这里可以配置别名、代理、构建选项等。比如我司后端API在/api路径下,本地开发需要代理:
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
server: {
proxy: {
'/api': {
target: 'http://dev.internal.company.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
},
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
}
}
})
这样在组件里就可以直接 fetch('/api/users'),不用写一长串URL,也避免了跨域问题。
状态管理:Pinia真香,Vuex可以退休了
以前用Vuex,写个模块要定义state、getters、mutations、actions,四件套齐活。现在Pinia一句话搞定:
// stores/user.ts
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
name: '',
avatar: '',
isLoggedIn: false
}),
actions: {
async login(credentials) {
const res = await api.post('/auth/login', credentials)
this.name = res.data.name
this.avatar = res.data.avatar
this.isLoggedIn = true
},
logout() {
this.$reset() // 一键重置状态!
}
}
})
在组件中使用也超简单:
<script setup lang="ts">
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
// 直接解构响应式数据(需要开启 enableReactive)
const { name, avatar } = storeToRefs(userStore)
// 调用方法
userStore.login({ username: 'foo', password: 'bar' })
</script>
最爽的是:Pinia天然支持TypeScript,不用再写一堆interface来约束state类型。 而且它没有mutations,所有状态变更都在actions里,逻辑更集中。
上周五晚上加班时,我还在纠结要不要用Vuex,结果同事一句“Vuex已经进入维护模式了”让我瞬间清醒。赶紧切到Pinia,代码量少了40%,测试覆盖率反而提高了。
路由管理:动态路由 + 权限控制实战
项目里有个需求:不同角色看到的菜单不一样。比如管理员能看到“用户管理”,普通员工只能看到“我的工单”。
Vue Router 4 支持动态添加路由,配合后端返回的权限列表,就能实现灵活的权限控制。
思路是这样的:
- 用户登录后,获取其拥有的权限(比如
['dashboard', 'ticket']) - 根据权限过滤预定义的路由表
- 调用
router.addRoute()动态注册
先定义基础路由:
// router/routes.ts
export const constantRoutes = [
{ path: '/login', component: () => import('@/views/Login.vue') },
{ path: '/404', component: () => import('@/views/404.vue') }
]
export const asyncRoutes = [
{
path: '/dashboard',
component: Layout,
meta: { roles: ['admin', 'user'] },
children: [{ path: '', component: () => import('@/views/Dashboard.vue') }]
},
{
path: '/user',
component: Layout,
meta: { roles: ['admin'] }, // 只有admin能看
children: [{ path: '', component: () => import('@/views/User.vue') }]
}
]
登录成功后处理权限:
// 在登录action中
const accessRoutes = asyncRoutes.filter(route =>
route.meta?.roles?.some(role => userRoles.includes(role))
)
accessRoutes.forEach(route => router.addRoute(route))
注意: 动态路由不会自动添加到导航菜单里!所以菜单组件也要根据router.getRoutes()动态生成,而不是写死。
有一次我忘了这茬,测试妹子点“用户管理”报404,我还以为是后端接口挂了,查了半天才发现是前端路由没加。当时真的想砸键盘……
组件通信:别再滥用$emit了!
Vue 3里,$emit 虽然还能用,但更推荐用defineEmits + v-model 或 provide/inject。
比如做一个带搜索的下拉框组件:
<!-- SearchSelect.vue -->
<script setup lang="ts">
const props = defineProps<{
modelValue: string
}>()
const emit = defineEmits<{
(e: 'update:modelValue', value: string): void
}>()
const handleInput = (val: string) => {
emit('update:modelValue', val)
}
</script>
<template>
<input v-model="modelValue" @input="handleInput" />
</template>
父组件使用:
<SearchSelect v-model="selectedUserId" />
是不是比 $emit('input', val) 清晰多了?而且TypeScript能完美推导类型。
对于深层嵌套组件(比如三级菜单),用 provide/inject 更优雅:
// 父组件
const theme = ref('dark')
provide('theme', theme)
// 子孙组件
const theme = inject('theme', 'light')
不过要注意:provide/inject 不是响应式的默认值,如果传的是原始值(如字符串),修改不会触发更新。所以建议传ref或reactive对象。
性能优化:别让页面卡成PPT
我们项目上线前做了一次Lighthouse审计,首屏性能只有58分。主要问题是:
- 首包太大(>1.2MB)
- 未懒加载路由
- 第三方库全量引入
解决方案:
路由懒加载
Vue Router天然支持:{ path: '/dashboard', component: () => import('@/views/Dashboard.vue') }组件按需引入UI库
比如Element Plus,默认全量引入会打包2MB+。用插件自动按需引入:// vite.config.ts import AutoImport from 'unplugin-auto-import/vite' import Components from 'unplugin-vue-components/vite' import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' export default defineConfig({ plugins: [ AutoImport({ resolvers: [ElementPlusResolver()] }), Components({ resolvers: [ElementPlusResolver()] }) ] })这样只用到的组件才会被打包,体积直降70%。
图片压缩 & CDN
把静态资源扔到公司CDN,再用vite-plugin-imagemin自动压缩。
优化后,首屏加载从3.2s降到0.9s,Lighthouse评分飙到92。运维大哥终于没在群里@我了,感动!
调试技巧:Chrome DevTools + Vue Devtools
前端调试离不开浏览器。除了常规的console.log(别笑,真香),我常用两个技巧:
在DevTools里直接修改组件状态
安装 Vue Devtools 插件后,可以在Components面板里直接编辑props、data,实时看效果。改完没问题再回代码里写,省得反复保存刷新。用
debugger断点 + Call Stack
遇到诡异bug时,在可疑函数开头加debugger,然后看调用栈是谁触发的。有一次某个computed属性疯狂重复计算,靠这招发现是父组件传了个新对象(引用变了),而不是改属性值。
另外,别信控制台的警告!Vue 3对ref解包很严格,经常报“Uncaught TypeError: Cannot read properties of undefined”。其实只要在模板里用.value,或者用storeToRefs,基本都能解决。
GitHub开源项目推荐
学习不能闭门造车。我在GitHub上扒了几个高质量Vue 3项目,代码规范、架构清晰,值得参考:
| 项目 | Stars | 特点 |
|---|---|---|
| vue-element-admin | 85k+ | 后台管理系统标杆,权限、多语言、Mock全齐 |
| naive-ui-vue3-template | 2k+ | 基于Naive UI的轻量模板,TS + Vite + Pinia |
| vitesse | 10k+ | Anthony Fu大神的作品,集成了最佳实践 |
我自己也建了个小项目练手:vue3-magic-box(名字随便起的),里面包含了上面提到的所有配置。欢迎Star(手动狗头)。
最后:摸鱼也要有技术追求
写这篇文章的时候,已经是凌晨1点。刚修复了一个因watch依赖数组没写全导致的数据不同步bug。虽然累,但搞定那一刻的爽感,比刷短视频强多了。
Vue生态看似庞杂,但核心思想没变:用更少的代码,做更多的事,同时保持应用的可维护性。 工具链再怎么变,理解原理才是王道。比如你知道Vite为什么快吗?因为它用原生ESM,跳过了打包环节。这种底层认知,能让你在选型时不被营销话术忽悠。
所以,即使在躺平摸鱼的日子里,我也坚持每天搞懂一个小知识点。毕竟,程序员的核心竞争力,从来不是加班时长,而是解决问题的能力。
好了,该去睡觉了。明天还得早起挤地铁,继续我的佛系搬砖生涯。如果你觉得这篇文章有点用,不妨点个赞?或者,直接fork我的GitHub项目,咱们一起摸鱼进步 😄

评论 0