从零开始构建一个现代化前端项目:我踩过的那些坑
去年年初,我在公司接手了一个全新的内部管理系统项目。这个系统需要兼容 PC 和移动端,支持主流浏览器,并且对性能和用户体验有较高要求。当时团队决定不再使用老旧的 jQuery + 模板引擎的老套路,而是重新规划技术栈,从零搭建一个现代化的前端项目。
作为一个已经写了几年前端的老程序员,我虽然经历过不少项目重构、架构设计的工作,但这次从零开始的过程,让我深刻体会到“自由”带来的选择成本有多高。今天想通过这篇文章,分享一下这段真实的经历,希望对正在或者将要进行类似项目的你有所帮助。
初期挑战:选型困难 + 团队分歧


刚开始最头疼的问题并不是写代码,而是如何选择合适的技术栈。现在前端发展太快了,各种框架、工具层出不穷,团队成员也各自有不同的偏好:
- 有人推荐 React,说社区大、生态成熟;
- 也有人倾向 Vue3,觉得语法更清晰上手快;
- 脚手架方面,是继续用 Webpack 吗?还是试试 Vite?
- 状态管理用 Redux 还是 MobX?或者干脆不用状态管理库?
- CSS 方案要不要引入 Tailwind 或者 CSS-in-JS?
这些看似简单的决策,实则关系到整个项目的可维护性和未来扩展性。
而且除了技术层面,我们还有不少实际需求要考虑:
- 必须兼容 IE11(客户那边真有遗留系统)
- 需要实现主题定制能力(客户想要换肤功能)
- 页面加载速度要尽量快(毕竟是个管理后台,操作频繁)
面对这些问题,我和团队开过几次会,也做了几个 demo 对比,最终选定了以下技术组合:
| 类别 | 技术选型 |
|---|---|
| 框架 | Vue3(Composition API) |
| 构建工具 | Vite + 基于 Rollup 的插件体系 |
| 包管理 | pnpm(替代 npm/yarn) |
| 状态管理 | Pinia(轻量,Vue 官方推荐) |
| 样式方案 | SCSS + BEM + 动态变量替换机制 |
| UI 组件库 | Element Plus(官方支持 Vue3) |
| 工程规范 | Prettier + ESLint + Husky + Commitizen + Lint-staged |
选定之后,就开始着手搭建工程结构和基础配置。
技术方案与实现思路

1. 初始化项目 & 构建工具配置
我们使用 Vite 创建了一个基本的 Vue3 项目:
pnpm create vite@latest my-project --template vue-ts
Vite 的速度快是真的爽,特别是开发服务器启动时间几乎为0,这在调试中节省了不少时间。对于生产构建部分,Vite 底层基于 Rollup,相比 Webpack 编译速度也更快,尤其适合现代前端项目。
为了兼容 IE11,我们在 vite.config.ts 中加入了对应的 target 配置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
build: {
target: 'es2015',
polyfillModulePreload: true,
},
})
不过光靠这个还不够,还需要手动引入 polyfill 来支持 Promise、fetch、Array.from 等新特性:
// main.ts 入口文件顶部加入
import 'core-js/stable'
import 'regenerator-runtime/runtime'
记得安装这两个包:
pnpm add -D core-js regenerator-runtime
2. 主题定制方案设计
为了让用户可以切换主题,我们没有直接使用像 Less 变量替换这种传统方式,而是尝试了一套动态 CSS 变量 + 主题类名控制的方式。
先定义一套全局变量:
:root {
--primary-color: #409EFF;
}
.theme-dark {
--primary-color: #1e88e5;
}
然后在 App.vue 里根据 store 中的主题值来切换 class:
<template>
<div :class="themeClass">
<router-view />
</div>
</template>
<script setup>
import { computed, watch } from 'vue'
import { useSettingsStore } from './stores/settings'
const settings = useSettingsStore()
const themeClass = computed(() => {
return settings.theme === 'dark' ? 'theme-dark' : ''
})
watch(() => settings.theme, (newTheme) => {
// 触发样式更新
})
</script>
这样所有组件都可以通过 CSS 变量 var(--primary-color) 使用动态颜色,不需要每种颜色都单独写逻辑判断。
3. 组件化开发实践
Element Plus 提供了很多好用的组件,但在使用过程中我们也发现了几个问题:
- 默认样式太重,有时候需要覆盖很多样式
- Table 组件在某些特定列的宽度计算不太准确
- Dialog 组件默认 z-index 太低导致在某些页面被遮挡
我们的解决办法是在项目中封装了一些公共组件,并统一处理掉这些兼容性问题。
例如自定义弹窗组件 BaseDialog.vue:
<template>
<el-dialog
v-model="visible"
:title="title"
:z-index="9999"
@close="$emit('close')"
>
<slot />
</el-dialog>
</template>
通过这种方式,统一调整样式层级问题,并提供一致的调用接口,减少后期维护成本。
开发过程中的那些“坑”
1. Vite 的热更新失效
在开发过程中经常遇到 HMR 不生效的情况。特别是在引入第三方库较多、项目结构较复杂的情况下,修改某些文件后页面并不会自动刷新。
解决方案是检查是否存在多个根实例或者异步组件未正确卸载。另外,Vite 官方建议使用 Vue 的 Composition API + <script setup> 形式开发,因为其 HMR 支持更好。
2. Polyfill 加载顺序导致白屏
之前我们把 core-js/stable 放到了入口文件的末尾,结果在部分低版本浏览器下出现白屏问题 —— 有些 promise 相关的代码执行前 polyfill 还没加载。
解决方法很简单:把 polyfill 放到入口文件的第一行,确保最先加载:
// main.ts
import 'core-js/stable'
import 'regenerator-runtime/runtime'
import { createApp } from 'vue'
import App from './App.vue'
...
3. 打包体积过大
一开始打包出来的 vendor 文件动不动就几 MB,严重影响首次加载体验。
我们做了一下优化:
- 使用按需导入(如 Element Plus 支持按需导入 + unplugin-vue-components 插件)
- 开启 gzip 压缩(配合 Nginx 配置)
- 将一些非核心依赖改为异步加载(比如图表库 echarts,采用异步加载 + Loading 占位)
最终首页打包 size 控制在 600KB 以内(gzip 后),首次加载平均 1.5s 左右完成渲染,满足需求。
最终效果与收益
经过近两个月的开发和打磨,项目上线后收到了不错的反馈:
- 用户反映页面响应流畅、交互体验明显优于老系统
- 新同事上手很快,因为代码结构清晰、组件复用性好
- 性能得分提升明显,Lighthouse 分数从之前的 50+ 上升到 80+
- 后续迭代效率高,新加功能模块只需套用模板,极大减少了重复劳动
更重要的是,这次从头搭建的过程中,我们沉淀出了一套自己的项目模板和开发规范,后续新项目可以直接基于这个模板快速启动,节省大量时间。
经验总结与建议

如果你也在考虑从零开始构建一个现代化前端项目,结合我的经验,给你一些建议:
技术选型不必一步到位,但要有一定的前瞻性
技术栈选择时既要考虑当下是否易上手,也要注意其活跃度和社区健康程度。比如 Vue3 / React18 都是目前主流选择,未来也有保障。尽早制定工程规范并落实到流程中
ESLint、Prettier、Git Hook 这些东西一定要提前配置好,否则后面再补代价更大。组件化思维贯穿始终,避免重复造轮子
公共组件抽象出来统一管理,不仅提高效率,还能保证一致性。性能优化不能忽视,但也不要过度优化
合理分包、图片压缩、懒加载都是必须做的;但对于非关键路径的功能,优先保证开发效率。多关注用户体验细节
比如 loading 状态的过渡、错误提示的友好性、动画的自然感等,这些“软实力”往往会带来意想不到的好评。
最后,我想说的是,从零搭一个项目其实是非常锻炼人的机会。它不仅仅是写代码,更是对整体工程能力、技术视野的一种考验。哪怕中间遇到了不少坑,回头看,那也是成长最快的阶段。
希望这篇文章对你有所启发。如果你也有类似的实践经验,欢迎留言交流!

评论 0