从传统行业转行后,我在杭州用 Vue.js 打怪升级的血泪史
去年夏天,我还在做供应链管理,每天和 Excel、ERP 系统打交道。30 岁那年,实在受不了半夜被叫起来处理“系统卡死”的电话,一咬牙裸辞学编程。三个月啃完 JavaScript 基础,又两个月折腾 Vue,如今在杭州一家中型互联网公司做前端——没错,就是阿里网易扎堆的那片区域。每天听着 Lo-fi Hip Hop 写代码,虽然头发少了点,但至少不用再背锅说“数据不对是 IT 的问题”。
上周五晚上十一点,产品经理突然在钉钉上@我:“这个新功能下周三上线,原型图刚画完,你看看能不能用 Vue 做个动态表单引擎?” 我盯着屏幕,差点把咖啡喷出来。但转念一想:这不正是检验我 Vue 生态掌握程度的好机会吗?
为什么是 Vue?一个“半路出家”者的务实选择
说实话,刚开始学框架时我也纠结过 React 和 Vue。但作为一个从传统行业跳过来的“高龄新人”,时间真的不多。Vue 的文档友好、上手快、社区中文资源多,对我这种没 CS 背景的人来说简直是救命稻草。更重要的是,杭州这边中小企业用 Vue 的比例相当高——毕竟不是每家公司都能养得起 React 专家团队。
而且 Vue 的响应式系统太香了!还记得第一次用 ref 和 reactive 实现数据联动时,那种“原来前端也能这么优雅”的震撼感。虽然现在看可能很基础,但对当时的我来说,就像打开了新世界的大门。
项目实战:搞个可配置的动态表单引擎
这次的需求其实挺典型:运营同学需要一个后台,能自己拖拽字段(输入框、下拉框、日期选择器等),保存成模板,然后用户端按模板渲染表单并提交。听起来简单?但细节全是魔鬼。
第一坑:状态管理到底用 Pinia 还是 Vuex?
团队里老哥说:“Vuex 已经过时了,Pinia 才是未来。” 可我翻了下项目历史,发现半年前另一个模块用的是 Vuex,而且没人愿意重构。为了不制造“技术债双胞胎”,我决定统一用 Pinia —— 毕竟 Vue 官方都站队了,而且它的 TypeScript 支持更自然。
// stores/formBuilder.ts
import { defineStore } from 'pinia'
export const useFormBuilderStore = defineStore('formBuilder', () => {
const fields = ref<Field[]>([])
const addField = (type: string) => {
fields.value.push({
id: Date.now().toString(),
type,
label: '',
required: false,
options: type === 'select' ? ['选项1', '选项2'] : undefined
})
}
const updateField = (id: string, updates: Partial<Field>) => {
const field = fields.value.find(f => f.id === id)
if (field) Object.assign(field, updates)
}
return { fields, addField, updateField }
})
小贴士:Pinia 的 setup 风格写法配合 Composition API,逻辑复用比 Vuex 的 modules 清晰太多。尤其适合我这种讨厌写
mapState的人。
第二坑:组件通信别再 props/$emit 到处乱飞!
表单预览区要实时反映编辑区的改动。一开始我用 v-model + emit,结果嵌套三层后,代码像意大利面条。后来灵机一动:直接把 store 里的 fields 拿来渲染不就行了?
<!-- FormPreview.vue -->
<template>
<div class="preview">
<component
v-for="field in fields"
:is="getFieldComponent(field.type)"
:key="field.id"
:field="field"
/>
</div>
</template>
<script setup>
import { useFormBuilderStore } from '@/stores/formBuilder'
const { fields } = useFormBuilderStore()
</script>
省去了层层传递,性能还更好——因为 Pinia 的 state 是响应式的,任何修改都会自动触发重渲染。产品经理看到预览区实时同步,眼睛都亮了:“这比上次那个 Java 后台做的快多了!”
第三坑:动态组件 + 异步加载 = 包体积爆炸?
每个字段类型对应一个组件(InputField.vue, SelectField.vue...)。如果全量引入,首屏 JS 会超 500KB。于是祭出 动态导入 + Suspense:
// components/FieldComponents.ts
export const fieldMap: Record<string, any> = {
text: defineAsyncComponent(() => import('./fields/InputField.vue')),
select: defineAsyncComponent(() => import('./fields/SelectField.vue')),
date: defineAsyncComponent(() => import('./fields/DateField.vue')),
}
配合 Webpack 的魔法注释还能自定义 chunk 名:
defineAsyncComponent(() => import(/* webpackChunkName: "field-input" */ './fields/InputField.vue'))
最终主包缩小了 40%,Lighthouse 分数从 68 提到 89。运维小哥终于没再吐槽“前端又把 CDN 带宽打满了”。
生态工具链:那些让我少掉 100 根头发的神器
Vite:开发服务器快到飞起
以前用 Webpack,改一行 CSS 要等 3 秒。自从迁移到 Vite,HMR 基本秒级响应。尤其适合我这种喜欢边听《Blinding Lights》Remix 边调试的人——节奏一上来,代码 flow 跟着走。
// vite.config.ts 关键配置
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src')
}
},
build: {
rollupOptions: {
output: {
manualChunks: {
// 分离第三方库
vendor: ['vue', 'pinia', 'axios'],
// 分离 UI 库
ui: ['element-plus']
}
}
}
}
})
ESLint + Prettier:拯救我的代码洁癖
作为前传统行业从业者,我深知“规范”的重要性。以前写 Excel 公式都要对齐缩进,现在写 JS 更不能乱来。团队强制启用 eslint-plugin-vue,连 template 里的属性顺序都管:
// .eslintrc.json
{
"extends": ["plugin:vue/vue3-recommended"],
"rules": {
"vue/max-attributes-per-line": ["error", { "singleline": 3 }]
}
}
虽然一开始觉得烦,但当我在凌晨两点排查 bug 时,清晰的代码结构真的救了我。
性能优化:别让 Vue 成为“慢”前端的借口
很多人以为 Vue 自带性能优化,其实不然。我们项目曾因一个列表渲染导致页面卡顿,Chrome DevTools 一开,FPS 掉到 15。
解决方案三板斧:
v-memo缓存静态节点(Vue 3.2+)<div v-for="item in list" :key="item.id" v-memo="[item.id, item.status]"> <!-- 只有 id 或 status 变化时才更新 --> </div>虚拟滚动:超过 100 条就上
vue-virtual-scroller懒加载图片:用
Intersection Observer自己封装了个LazyImage组件
| 优化手段 | 首屏加载时间 | 滚动 FPS |
|---|---|---|
| 优化前 | 2.8s | 18 |
| 仅 Vite 分包 | 1.9s | 25 |
| + 虚拟滚动 | 1.7s | 52 |
| + v-memo | 1.6s | 58 |
测试妹子跑来问我:“你们前端最近加了什么黑科技?操作丝滑好多!” 其实哪有什么黑科技,不过是把文档里的 best practices 老老实实用了一遍罢了。
云原生时代的前端:K8s 不只是后端的事
作为前传统行业人,我对部署流程特别敏感。以前发个版本要填 5 张审批单,现在我们 CI/CD 流水线跑在 K8s 上,前端构建镜像自动推送到 Harbor,Helm 一键部署。
但有一次,前端镜像太大(1.2GB!),导致 Pod 启动超时。查了半天发现是 node_modules 全打包进去了。后来改成多阶段构建:
# 构建阶段
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# 运行阶段
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
# 镜像大小从 1.2GB → 28MB!
运维老哥拍我肩膀:“不错啊,前端也开始考虑 infra 了。” 其实我只是不想半夜被 PagerDuty 叫醒而已。
代码人生:30 岁转行,值不值?
经常有人问我:“30 岁学编程是不是太晚了?” 我的回答是:只要别用年龄限制自己,什么时候都不晚。
Vue 生态教会我的不仅是技术,更是一种“渐进式”的思维——从小功能做起,逐步迭代,不断优化。这和我之前做供应链优化的思路惊人地相似:先解决最痛的点,再考虑扩展性。
当然,过程肯定狼狈。记得第一次线上事故是因为忘了 key 导致列表渲染错乱,被 QA 围着问了半小时。但正是这些坑,让我从“会写代码”变成“懂工程”。
如今坐在杭州的写字楼里,窗外是西溪湿地,耳机里放着 The Weeknd,手里调着 Element Plus 的主题色。虽然工资还没达到“阿里 P7”的水平,但至少,我在掌控自己的代码人生。
最后几句真心话
如果你也是半路转行的新人,别怕。Vue 的生态足够包容,社区足够温暖。遇到问题先看官方文档(真的,90% 的问题里面都有答案),再搜 GitHub Issues,最后才是 Stack Overflow。
记住:前端不只是切图仔,更是用户体验的守门人。每一个 v-if 的判断、每一个 watch 的监听,背后都是真实用户的等待与期待。
好了,产品又在钉钉上@我改需求了。这次他说:“能不能加个 AI 自动生成表单的功能?” …… 我默默打开了 VS Code,顺手把音乐切到了《Eye of the Tiger》。
(全文完)

评论 0