Vue.js 生态系统深度探索与项目实战:一个佛系程序员的摸鱼学习笔记

Swagger抄写员
2025-12-17 17:49
阅读 213

早上8点,北京地铁10号线又挤得像沙丁鱼罐头。我一边单手抓着扶手,一边用另一只手刷GitHub Trending——是的,这就是我这个“躺平但技术不能停”的程序员日常。通勤一小时,不看点代码总觉得亏了,毕竟工资不是白拿的(虽然我总觉得自己在摸鱼)。

最近公司有个新项目要上,是个内部管理后台,技术栈指定Vue 3 + TypeScript。说实话,之前一直在用Vue 2,对Composition API只是“听说过”,连refreactive的区别都快记混了。产品经理上周五下班前突然甩来一句:“下周三要能跑起来,不然双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 支持动态添加路由,配合后端返回的权限列表,就能实现灵活的权限控制。

思路是这样的:

  1. 用户登录后,获取其拥有的权限(比如 ['dashboard', 'ticket']
  2. 根据权限过滤预定义的路由表
  3. 调用 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)
  • 未懒加载路由
  • 第三方库全量引入

解决方案:

  1. 路由懒加载
    Vue Router天然支持:

    { path: '/dashboard', component: () => import('@/views/Dashboard.vue') }
    
  2. 组件按需引入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%。

  3. 图片压缩 & CDN
    把静态资源扔到公司CDN,再用vite-plugin-imagemin自动压缩。

优化后,首屏加载从3.2s降到0.9s,Lighthouse评分飙到92。运维大哥终于没在群里@我了,感动!


调试技巧:Chrome DevTools + Vue Devtools

前端调试离不开浏览器。除了常规的console.log(别笑,真香),我常用两个技巧:

  1. 在DevTools里直接修改组件状态
    安装 Vue Devtools 插件后,可以在Components面板里直接编辑props、data,实时看效果。改完没问题再回代码里写,省得反复保存刷新。

  2. 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

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝