从零开始构建一个现代化前端项目

工单终结者
2025-06-28 04:43
阅读 749

从一次项目重构说起:我是如何一步步搭起现代前端项目的骨架的?

从一次项目重构说起:我是如何一步步搭起现代前端项目的骨架的?

去年我加入一个老项目组,接手的是一个运行了五年多的前端项目。说实话,刚打开代码目录那一刻,我就有点懵了——满屏的 jQuery、散落各处的脚本引入、毫无模块化的样式文件,还有那些被复制粘贴过 N 遍的工具函数……这不是项目,这是一座随时会塌的积木塔。

团队里的人对这个项目早已是又爱又恨。业务需求一直在变,但代码却越来越难维护。最要命的是每次上线都像走钢丝,改个小功能可能引发好几个连锁问题。更别说什么构建优化、自动化测试这种“高级货”,压根不在我们考虑范围内。

于是我和团队决定:重新来过,用现代化的方式,搭建一个新的前端架构。虽然不能一步到位推翻重写,但我们希望以最小成本,把项目逐步迁移到新架构中。下面我想跟大家分享一下这段经历中的思考、踩过的坑和最后沉淀下来的技术方案。


为什么需要重新开始

我们的原始项目结构非常典型:

- index.html
- js/
    - common.js
    - util.js
    - page1.js
    - page2.js
- css/
    - main.css
    - reset.css
- images/

每个页面单独引入几个 JS 文件,CSS 直接在 HTML 中通过 <link> 加载。开发流程完全靠人肉检查、手动打包资源,部署则是直接上传所有文件到服务器某个目录。

在这种结构下,遇到的问题包括但不限于:

  • 修改一个工具函数会影响多个页面
  • 页面加载慢,JS 和 CSS 没有合理拆分
  • 代码无法复用,重复代码满天飞
  • 没有任何本地开发环境,全靠浏览器刷新调试
  • 新人上手门槛极高

这些问题就像一个个慢性病,在不影响系统运转的情况下持续消耗团队士气。最终推动我们动手的原因,是产品提了一个新的交互模块——它需要使用一些现代框架的能力(比如 React),而老结构根本支撑不了。


我们的目标与选型思路

既然要做一次彻底的革新,那我们就得想清楚目标到底是什么:

  1. 可维护性:代码结构清晰,便于多人协作
  2. 性能优秀:减少首屏加载时间,提升用户体验
  3. 开发友好:有热更新、代码提示、类型检查等开发支持
  4. 渐进式迁移:不抛弃现有业务,逐步替换
  5. 构建可控:能够灵活拆包、分析体积、监控质量

基于这些目标,我们选用了如下技术栈组合:

模块 技术选型 原因
构建工具 Vite 快速冷启动,开箱即用
模块化 ES Modules + TypeScript 现代标准,类型安全
UI 框架 React (如果已有 Vue 或其他也保留) 提供组件化能力
样式管理 SCSS + CSS-in-JS (可选) 提高复用性
包管理 pnpm 更快依赖安装
工程规范 ESlint + Prettier + Git Hook 统一风格
自动化 GitHub Actions CI/CD 减少人工失误

之所以没有一开始就全面切换到 Vue 或 React 的完整生态,是因为我们要给不同小组选择的空间。但核心架构必须统一,这样才能保证后续的兼容性和协同效率。


实战搭建过程

Step 1:搭建基础工程结构

我们创建了一个新的项目仓库,命名为 frontend-core,作为整个架构的种子项目:

vite create frontend-core --template react-ts
cd frontend-core
pnpm install

这样就快速得到了一个 TypeScript + React + Vite 的基础模板。

接着,我们在原有项目中设置软链接,让新旧两个结构可以共存:

ln -s ../frontend-core/src new-ui

然后通过修改入口 HTML 页面,动态加载新模块,实现逐步替换单页内容的目的。这种方式既降低了风险,也能让业务持续推进。

Step 2:配置构建与开发环境

Vite 已经内置了很多现代构建特性,但为了满足定制化需求,我们补充了一些配置:

// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
  build: {
    outDir: '../dist',
    sourcemap: true,
    rollupOptions: {
      output: {
        manualChunks(id) {
          if (id.includes('node_modules')) {
            return id.includes('lodash') ? 'vendor-lodash' : 'vendor'
          }
        }
      }
    }
  },
  server: {
    port: 3000,
    open: true
  }
})

现代网页界面设计示例-1

这个配置帮助我们将 vendor 分离,并开启源码映射方便调试,同时也设定了本地服务端口。

Step 3:制定编码规范并自动执行

为了避免新架构变成“新瓶装旧酒”,我们在项目中集成 ESlint + Prettier:

pnpm add eslint prettier eslint-config-prettier eslint-plugin-react @typescript-eslint/eslint-plugin @typescript-eslint/parser --save-dev

然后配置 .eslintrc.js

module.exports = {
  root: true,
  env: {
    browser: true,
    es2021: true
  },
  extends: [
    'eslint:recommended',
    'plugin:react/recommended',
    'plugin:@typescript-eslint/recommended',
    'prettier'
  ],
  parser: '@typescript-eslint/parser',
  plugins: ['react', '@typescript-eslint'],
  rules: {
    // 定制规则
  }
}

再结合 Husky 设置 Git Commit 时自动格式化:

npx husky-init && pnpm install
npx husky add .husky/pre-commit "pnpm lint"

这样每次提交前都会做一次静态检查,大幅降低低级错误的发生几率。


踩坑记录与解决方案

在整个过程中,我们也遇到了不少实际问题。分享几个印象特别深的:

🧱 CSS 全局污染问题

早期我们尝试在 React 组件中直接引用全局样式,结果导致样式冲突频发。例如:

import './MyComponent.scss'

这个问题的根源在于 SCSS 文件如果没有使用 @use@forward,就会默认注入全局作用域。为解决这个问题,我们做了两件事:

  1. 使用 CSS Module:
import styles from './MyComponent.module.scss'

function MyComponent() {
  return <div className={styles.container}></div>
}
  1. 推行 BEM 命名规范,即使不使用 CSS Module,也避免类名重叠。

🕳️ 第三方依赖版本冲突

有一个功能需要用到 dayjs,但因为之前项目已经依赖了较老的版本,升级后部分旧代码失效。我们最终采用了以下策略:

  • 对新项目强制使用最新版 dayjs
  • 在老模块中通过别名引入旧版本
resolve: {
  alias: {
    dayjs$: path.resolve(__dirname, 'node_modules/dayjs')
  }
}

不过这只是权宜之计,最终目标还是推动统一版本。

🔌 浏览器兼容问题

尽管现代框架默认支持大多数主流浏览器,但在我们客户的环境中,仍有约 5% 的用户在使用 IE11。我们不得不做一些适配处理:

  • 引入 Polyfill:
pnpm add @vitejs/plugin-legacy

并在 Vite 配置中启用:

import legacy from '@vitejs/plugin-legacy'

plugins: [
  legacy({
    targets: { ie: '11' },
    additionalLegacyPolyfills: ['regenerator-runtime/runtime']
  })
]

虽然带来了额外体积,但也让我们能安心交付。


成果与反思

经过三个月的努力,我们现在的新架构已经支撑起了 70% 的新功能开发。老项目仍然在线上运行,但新需求全部优先跑在这个新架构之上。

具体的收益包括:

  • 构建速度提升了 80%,开发者体验改善明显
  • 首屏加载时间从平均 3.5s 缩短到 1.2s
  • 团队代码质量和一致性大幅提升
  • 多个项目共享一套架构,协同效率提高

更重要的是,现在我们可以更容易地接入 CI/CD、自动化测试、代码覆盖率监控等工程实践。


我的建议:给正在起步的你

如果你也正准备搭建一个新的前端项目,或者想改造旧有架构,这里是我的几点建议:

  1. 不要一开始就追求大而全
    我们一开始也想过一次性引入微前端、Monorepo、Serverless 这些热门词,但最终发现,先从小处着手,先把基础打好,是最稳健的做法。

  2. 架构服务于业务
    不要为了“新技术”而“新技术”。比如如果你的项目并不复杂,React 可能反而是一种负担,直接用纯 TS + DOM 操作更轻便。

  3. 注意过渡期的平稳切换
    如果是老项目升级,记得设计好新旧模块通信机制和共存逻辑。你可以借助 Web Component、模块联邦等方式做隔离。

  4. 重视开发者的体验
    一个好架构不仅要运行得好,更要让人用得舒服。所以像热加载、错误提示、IDE 插件支持这类细节,一定要提前规划。

  5. 保持开放和迭代思维
    架构不是一成不变的。半年后回头看当前的设计,你会发现很多可以优化的地方。重要的是保持学习和改进的动力。


写在最后

从零开始搭建一个前端项目,听起来好像很酷,但背后的挣扎和试错只有真正做过的人才知道。不过当我看到同事们第一次用上自动格式化、看到页面秒级热更新的时候,真的觉得这一切都是值得的。

技术架构只是工具,背后真正重要的,是一群愿意一起进步的人。希望这篇文章能给你一些启发,少走点弯路。如果你正在自己的项目中尝试类似的事情,欢迎留言交流,我们一起成长 😊

评论 0

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