从零搭建前端项目,一个文科生的血泪实战

表结构守护者
2025-12-29 03:25
阅读 290

上个月底,我刚入职新公司两个月,老板突然在周会上扔来一个需求:“小张,下个季度我们要上线一个新的运营活动平台,你来负责前端架构搭建。”我当时心里咯噔一下——作为一个非科班出身、靠自学转码的文科生,虽然已经能熟练写 React 组件了,但“从零搭建项目”这种活儿,听起来就带着一股“线上崩了你背锅”的压迫感。

更扎心的是,这个平台要支撑全年的营销活动,比如618、双11那种流量洪峰。产品经理还美其名曰:“轻量级 MVP,先跑起来再说。”可我知道,一旦上线,运营同学会像蝗虫一样涌进来配置各种弹窗、抽奖、倒计时……代码要是烂,后期维护就是地狱。

但没办法,饭碗要紧。而且说实话,我也想证明给团队看看:文科生也能写出干净、可维护、扛得住压力的前端工程。

于是,我花了两周时间,边听 Billie Eilish 的新专辑边折腾,最终搭出了一套现代化前端项目骨架。今天就来聊聊这段“从零开始”的实战经验,顺便吐吐槽、踩踩坑。


技术选型:不是越新越好,而是“够用+好改”

很多新人(包括半年前的我)一听说“现代化”,立马冲去 GitHub 看 Star 数最高的框架,结果搭完发现团队没人会修 Bug,或者和现有系统完全不兼容。这次我学乖了:技术选型的核心不是炫技,而是“可持续交付”

我们的核心诉求很明确:

  • 快速开发运营活动页(大量动态配置)
  • 支持微前端(未来可能接入其他业务线)
  • 代码可读性高(毕竟我经常被叫去解释为什么某个按钮没对齐)
  • 构建速度不能太慢(测试同学等不及)

下面是我对比的几个关键环节:

框架:React vs Vue3

维度 React (18+) Vue3
团队熟悉度 70% 用过 30% 用过
生态丰富度 高(尤其 UI 库) 中高
学习曲线 JSX 对新手稍陡 模板语法更直观
微前端支持 完善(qiankun 等) 也支持,但社区案例少些

我们团队之前项目都是 React,加上 Ant Design Pro 有现成的运营后台模板,最终还是选了 React 18 + TypeScript。虽然我个人觉得 Vue 的 <script setup> 写起来更爽,但“团队一致性”比个人喜好重要多了——别忘了,我是唯一一个非科班,得降低协作成本。

构建工具:Vite 还是 Webpack?

去年我还在用 Create React App(Webpack),每次改一行 CSS 都要等 10 秒热更新,简直想砸键盘。这次果断试了 Vite

实测效果:

  • 启动时间:Webpack 25s → Vite 1.2s
  • HMR 更新:Webpack 3-5s → Vite <300ms

而且 Vite 对 TypeScript、CSS Modules、SVG Sprite 的原生支持非常友好,几乎不用配。唯一的小坑是:某些老插件(比如 html-webpack-plugin 的替代品)需要找 Vite 版本,不过 GitHub 上基本都有人写了。

状态管理:Zustand 走起!

以前我迷信 Redux,写一堆 action、reducer、selector,结果运营页面状态其实很扁平——要么是活动配置,要么是用户行为记录。Redux 那套仪式感太重了。

这次用了 Zustand,几行代码搞定全局状态:

// stores/activityStore.ts
import { create } from 'zustanding';

interface ActivityState {
  config: ActivityConfig | null;
  setConfig: (config: ActivityConfig) => void;
}

export const useActivityStore = create<ActivityState>((set) => ({
  config: null,
  setConfig: (config) => set({ config }),
}));

用的时候直接 const { config } = useActivityStore();,清爽得像喝冰阔落。而且它自动做 memoization,性能完全不用担心。

UI 组件库:Ant Design Pro 还是自己造轮子?

运营后台需要表格、表单、图表、权限控制……如果从零写,我估计得加班到春节。好在 Ant Design Pro 提供了完整的中后台解决方案,连国际化、动态菜单都配好了。

但!千万别直接用默认主题色。上次产品经理看到蓝色后台,说“不够喜庆”,硬让我改成红色。结果全局覆盖样式搞了两天,差点抑郁。现在我的建议是:初期用 Pro,但尽早抽离主题配置


目录结构:为“未来接手的人”写代码

我见过太多项目,src 里堆了 50 个文件,名字全是 utils1.jshelper_new_v2.js……作为注重可读性的文科生,我坚持一点:代码是写给人看的,机器只是顺便执行一下

最终目录长这样:

src/
├── assets/            # 静态资源
├── components/        # 通用组件(Button, Modal)
├── features/          # 业务功能模块(每个活动一个文件夹)
│   ├── lucky-draw/    # 抽奖活动
│   └── countdown/     # 倒计时
├── hooks/             # 自定义 Hook
├── layouts/           # 布局
├── pages/             # 路由页面
├── services/          # API 请求
├── stores/            # 状态管理
├── styles/            # 全局样式 & 主题
├── types/             # TypeScript 类型定义
└── utils/             # 工具函数(带单元测试!)

特别说明:

  • features/ 是亮点!每个运营活动独立封装,互不干扰。运营同学要加新玩法?直接新建一个文件夹,不影响旧逻辑。
  • 所有 API 调用走 services/,统一处理 loading、error、mock。
  • utils/ 里的函数必须有 JSDoc 注释,比如:
/**
 * 格式化金额,保留两位小数,千分位逗号分隔
 * @example formatMoney(123456.789) → "123,456.79"
 */
export const formatMoney = (num: number): string => {
  // ...
};

性能优化:别让运营拖垮用户体验

运营最爱搞“花里胡哨”的动画和图片,但用户可不管这些——他们只关心“点按钮有没有反应”。

我做了三件事:

1. 图片懒加载 + WebP

react-intersection-observer 实现懒加载,配合后端返回 WebP 格式。体积平均减少 40%,首屏加载快了不少。

import { useInView } from 'react-intersection-observer';

const LazyImage = ({ src, alt }) => {
  const [ref, inView] = useInView();
  return (
    <img
      ref={ref}
      src={inView ? src : placeholder}
      alt={alt}
      loading="lazy"
    />
  );
};

2. 动态导入运营模块

不是所有活动都要一开始就加载。用 React.lazy + Suspense 按需加载:

const LuckyDraw = lazy(() => import('@/features/lucky-draw'));

function App() {
  return (
    <Suspense fallback={<Loading />}>
      {currentActivity === 'lucky' && <LuckyDraw />}
    </Suspense>
  );
}

3. 防抖提交

运营页面常有“保存配置”按钮。我加了防抖,避免手滑连点十次,把后端打挂:

const debouncedSave = useMemo(
  () => debounce((data) => api.saveConfig(data), 500),
  []
);

GitHub 工作流:别让 PR 变战场

我们团队强制要求:所有代码必须通过 PR 合并,且至少一人 review。

为了减少扯皮,我在 .github/PULL_REQUEST_TEMPLATE.md 里写了清晰的 checklist:

- [ ] 自测通过(包括边界 case)
- [ ] 单元测试覆盖率 ≥80%
- [ ] 无 console.log
- [ ] 文案已交给产品确认
- [ ] 性能影响评估(如有)

另外,用 ESLint + Prettier + Husky 在提交前自动格式化,避免“空格党”和“制表符党”打架。文科生表示:统一风格,世界和平。


真实踩坑:那些文档不会告诉你的事

  1. Vite + Ant Design Pro 的图标问题
    Pro 默认用 @ant-design/icons,但 Vite 不识别动态导入。解决办法:显式引入图标,或者用 vite-plugin-antd-icon

  2. 运营配置的 JSON Schema 校验
    有次运营填了个非法的正则表达式,导致整个页面白屏。后来我在 zod 里加了严格校验:

    const ActivityConfigSchema = z.object({
      title: z.string().min(1),
      regex: z.string().regex(/^\/.*\/[gimuy]*$/), // 必须是合法正则字符串
    });
    
  3. Safari 的 Date 陷阱
    “2024-06-18” 这种日期字符串在 Safari 里解析成 Invalid Date!必须转成 “2024/06/18” 或用 dayjs 处理。文科生第一次遇到时,真的以为是电脑坏了。


效果与反思

上线两周,支撑了三次小型运营活动,0 P0 事故。最让我自豪的是:另一个同事接手一个新活动模块时,只用了半天就跑通了全流程——他说:“目录太清晰了,像读小说一样顺。”

当然,还有很多不足。比如还没接入前端监控(Sentry)、自动化部署流程也手动。但作为非科班,能在两个月内交出这样的答卷,我已经挺直腰杆了。

最后想对和我一样的转码朋友说:文科生的共情力和细节控,其实是前端的巨大优势。我们更懂用户怎么想,更在意界面是否“顺眼”,更愿意写注释——这些,才是好代码的灵魂。

GitHub 仓库我脱敏后会开源(等老板批准),欢迎来 star 和吐槽。毕竟,写代码的路上,谁还不是一边崩溃一边进步呢?

写完这篇博客,已经是凌晨一点。耳机里 Billie Eilish 唱着:“I’m not your friend or anything… damn.”
我笑了笑,按下保存,关掉编辑器。明天还要和产品经理 battle 一个“能不能加个呼吸灯效果”的需求。

评论 0

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