从零开始构建一个现代化前端项目的实战经验分享

后端便利贴
2025-06-23 01:39
阅读 648

开篇:为什么这次重构让我重新思考了前端开发的起点

开篇:为什么这次重构让我重新思考了前端开发的起点

今年年初,我参与了一个全新的企业级 SaaS 应用项目,目标是为一家中型制造企业提供生产流程数字化解决方案。虽然公司内部已经有几个老系统在运行,但性能瓶颈和维护成本逐渐显现出来,管理层决定推倒重来——从头构建一个全新的前端架构。

说白了,这个任务听起来很爽:“从零开始”意味着可以摆脱历史包袱、使用新技术、按照理想方案来设计。但在实际执行过程中,我发现事情远没有那么简单。作为一个全栈开发者,不仅要兼顾前后端的协同,更要站在团队的角度考虑技术选型、开发效率和长期可维护性。

这篇文章我想通过这次真实的项目经历,聊聊如何真正地“从零开始”构建一个现代化前端项目,并不是简单地敲几条命令生成脚手架工程,而是从底层架构到上层业务的一整套设计与实现思路。


背景:项目背景与挑战初现

背景:项目背景与挑战初现

新项目的目标用户群体明确:一线工厂操作人员、班组长和工厂管理者。界面需要支持数据展示、表单提交、权限控制、图表分析等一系列功能。同时产品侧要求:

  • 主要浏览器兼容(Chrome 60+,Edge 最新版,Safari)
  • 支持低网速下的基础可用
  • 移动端优先,部分功能需适配平板操作
  • 需要集成微前端架构以对接未来可能接入的其他系统模块

最初,我们只有一张画在 Figma 上的 UI mockup 和一版不太详细的产品需求文档。这意味着我必须从头规划整个前端结构,同时还要确保后续开发人员能够快速上手并扩展系统功能。


挑战1:如何选择现代框架 + 微前端架构?

挑战1:如何选择现代框架 + 微前端架构?

第一个难题就是技术选型

团队成员普遍有 React 的使用经验,但也有对 Vue 的兴趣。最终我们选择 React,原因是:

  • 大厂生态支持(如 Next.js、React Query、TanStack Router 等工具链完善)
  • 社区活跃度高,遇到问题解决路径清晰
  • 适合做组件化开发,便于未来拆成微前端模块

但我们还需要考虑未来的微前端架构集成能力。因此,在项目初期就引入了 Module Federation(Webpack 5 原生支持) 技术作为微前端方案的基础。

说实话,在项目刚开始时很多人不理解为何需要这么早就考虑微前端的问题。我的逻辑很简单:

“一个现代化前端应用不应该一开始就设计成孤岛,而应具备灵活组合的能力。”

于是我们在项目骨架搭建时,不仅选用了 Vite + React 的组合,还提前配置了 Module Federation 插件,让每一个 feature module 尽早拥有被独立部署的能力。

不过这也带来了新的复杂度,比如跨模块通信、样式隔离、路由管理等问题,这后面再讲。


挑战2:从零开始,到底需要哪些基础设施?

接下来就是搭建项目的初始结构。我习惯的做法是:

npm create vite@latest my-saas --template react-ts

然后手动添加 ESLint、Prettier、Jest、React Testing Library、TypeScript 支持等。这些看似繁琐,却是保障代码质量的关键。

文件结构建议(参考 Airbnb 和一些开源项目)

我们的结构如下:

src/
├── assets/           # 图片、字体、SVG图标资源
├── components/       # 公共组件
│   ├── atoms/        # 原子组件,按钮、输入框等
│   ├── molecules/    # 分子组件,由原子组装而成的功能单元
│   └── organisms/    # 组织组件,页面级别的组合
├── hooks/            # 自定义 Hook
├── layouts/          # 页面布局组件
├── pages/            # 各个页面文件
├── services/         # 接口封装(Axios)
├── stores/           # Zustand / Redux Toolkit 状态管理
├── routers/          # 路由定义(TanStack Router)
├── utils/            # 工具函数,如 dateFormat、formatCurrency 等
└── App.tsx

这种结构在团队协作中非常实用,新人可以快速找到自己负责的部分。

但当时我们也走过弯路。一开始我们尝试用 pages/* 直接作为路由自动加载模块,后来发现随着业务增长,这种方式难以管理权限、懒加载和动态导入。

最终我们改用 TanStack Router + 动态导入的方式实现按需加载,代码简洁且可拓展性强。


挑战3:状态管理和接口分层怎么做最合理?

状态管理方面,我们选择了 Zustand。相较于 Redux Toolkit,它更轻量,写法也更接近于 React 的心智模型。而且配合 TypeScript 使用起来非常舒服。

示例代码:

import { create } from 'zustand'

type UserState = {
  user: User | null
  fetchUser: (id: string) => void
}

const useUserStore = create<UserState>((set) => ({
  user: null,
  fetchUser: async (id) => {
    const res = await api.get(`/users/${id}`)
    set({ user: res.data })
  },
}))

export default useUserStore

接口分层这块,采用的是经典的 Repository Pattern + Axios Interceptor。

// src/services/api.ts
import axios from 'axios'

const apiClient = axios.create({
  baseURL: '/api',
  timeout: 5000,
})

// 请求拦截器,添加 token
apiClient.interceptors.request.use((config) => {
  const token = localStorage.getItem('auth_token')
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
})

// 响应拦截器处理错误码
apiClient.interceptors.response.use(
  (res) => res,
  (error) => {
    if (error.response?.status === 401) {
      // 执行跳转登录页等操作
    }
    return Promise.reject(error)
  }
)

export default apiClient

然后每个业务模块都有自己的 service 文件,例如:

// src/services/user.service.ts
import apiClient from './api'

export const getUser = async (userId: string) => {
  const res = await apiClient.get(`/users/${userId}`)
  return res.data
}

这种方式让接口请求统一归口,便于调试和维护。


挑战4:用户体验细节不容忽视

作为一名经历过多个交付项目的工程师,我深知有时候一个小小的体验优化就能大幅提升用户满意度。特别是在工业场景中,很多工人会反复进行某些固定操作,哪怕是多一次点击或等待时间长了一点,都会影响他们的效率。

我们做了以下几点体验提升:

1. 表单校验增强 + 实时反馈

使用了 React Hook Form,配合 Yup 做 schema 校验。

优势在于无需频繁触发渲染,性能好;并且可以在后端返回格式错误时精准定位提示。

import { useForm } from 'react-hook-form'
import * as yup from 'yup'
import { yupResolver } from '@hookform/resolvers/yup'

const schema = yup.object().shape({
  name: yup.string().required('必填'),
  email: yup.string().email('请输入正确的邮箱').required(),
})

function MyForm() {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm({ resolver: yupResolver(schema) })

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('name')} />
      {errors.name && <span>{errors.name.message}</span>}
    </form>
  )
}

2. Loading 状态精细化处理

对于关键操作(如保存数据),我们采用了全局 loading + 局部 loading 区分,防止用户误操作。

全局 loading 我们用的是 Zustand + Context Provider 构建的状态共享机制。

局部 loading 则是在按钮级别控制,比如提交之后禁用按钮防止重复提交。

3. 防止页面无响应(FOUC)

这个问题在使用动态导入路由时偶尔出现,尤其是 Safari 浏览器上更为明显。我们通过预加载策略 + 骨架屏组件缓解了这个问题。


挑战5:性能优化与兼容性考量

前端项目的终极考验之一永远是性能表现,尤其是在国内复杂的网络环境和老旧设备上。

我们主要做了以下几件事:

1. 使用 Web Vitals 进行监控

通过引入 Google 的 Web Vitals 检测库,实时收集用户的 LCP、CLS、FID 等指标。

import { onCLS, onFID, onLCP } from 'web-vitals'

onCLS(console.log)
onFID(console.log)
onLCP(console.log)

2. 代码分割 + 图片懒加载

所有非首屏内容都进行了懒加载处理,图片资源则使用 <img loading="lazy" /> 或者 <LazyLoad> 组件包裹。

3. Tree Shaking & Polyfill 降级处理

Vite 默认已经做了 Tree Shaking,但对于老版本浏览器,我们还是引入了 Babel 和 core-js 来垫一部分 ES 特性。

// babel.config.json
{
  "presets": [
    ["@babel/preset-env", { "useBuiltIns": "usage", "corejs": 3 }],
    "@babel/preset-react"
  ]
}

这样能保证 Chrome 60 这种旧浏览器也能顺利运行。


挑战6:团队协作与开发效率提升

项目做到中期时,随着团队扩大,沟通和代码风格冲突成为大问题。于是我推动了几项制度化的实践:

1. Git Commit 规范 + 提交前检查

我们制定了基于 Angular 的 commit message 规范(feat:xxx、fix:xxx 等),并通过 husky + lint-staged 在每次提交前进行代码格式检查。

npx mrm@2 lint-staged

这样即使多人协作也不会出现严重的风格混乱。

2. Storybook 可视化组件文档

为了让设计师和开发都能看到最新组件,我们接入了 Storybook 并逐步将通用组件纳入其中。

这对于 UI Review 十分有帮助,特别是当设计师想要快速查看组件 API 是否符合预期时。

3. E2E 测试自动化探索

虽然项目初期没有完全覆盖 E2E 测试,但我们使用了 Cypress 搭建了核心流程的测试用例,例如登录 + 查看列表 + 编辑信息等闭环操作。


效果总结:稳定、高效、可扩展的现代化前端架构

项目上线半年以来,整体反馈良好:

  • 首次加载平均耗时减少 38%
  • 用户操作卡顿投诉下降 85%
  • 新人学习成本降低约 40%,因为结构清晰、文档齐全
  • 支持多入口部署,已有两个模块作为微前端独立上线

最关键的是,我们避免了早期因架构设计不合理导致的大规模返工,也为后期的持续迭代打下了坚实基础。


经验分享:给正在从零搭建项目的你的建议

如果你也在准备启动一个新项目,不妨参考以下几个建议:

✅ 不怕慢,先定好结构和规范

刚开项目别急着写功能,先把目录结构、命名规则、代码规范、分支策略定下来。越早建立标准,越能避免后期大规模重构。

✅ 技术选型要适度前瞻,但不可盲目追求时髦

比如我们可以选择 React,因为它生态成熟,而不是为了尝鲜去尝试一个尚未稳定的框架。如果只是 CRUD 页面,甚至可以考虑 Vue 或者原生 JavaScript。

✅ 性能优化要前置,不是上线后再考虑

Lighthouse 是个好帮手,从开发第一天起就开始关注 performance score,不要等到用户吐槽才补救。

✅ 前端不止是写代码,更是用户体验的守护者

有时候比实现功能更重要的,是思考用户真实的操作场景。例如移动端键盘弹出遮挡输入框、网络延迟下提示语的设计、防抖节流策略的选择等,都是体现专业度的地方。


结语:从零开始不是终点,而是起点

回过头来看,这一次项目从零开始的过程,其实并不是简单的技术堆叠,而是对现代前端工程的一次全面理解和实践。在这个过程中,我经历了从最初的迷茫、纠结,到后来的坚定和技术自信。

或许下次你们接手类似任务的时候,也可以少走些弯路。愿你我都能写出既优雅又实用的代码,不只是完成工作,而是把前端这件事,做得更有价值。

最后送大家一句话:

“真正的现代化前端项目,不是技术堆砌出来的,而是在一次次权衡、妥协和坚持中打磨出来的。”

如果你喜欢这类实战向的文章,欢迎留言交流或者分享你的经验!

评论 0

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