前端选型与实践报告(2025)

1. 执行摘要【Executive Summary】

  • 目标:在保证可维护性与交付速度的前提下,确定企业级前端的通用技术栈、工程体系与治理策略,并给出落地路线图。
  • 总体结论

    • 新建 SPA/中后台优先 Vite +(React 或 Vue);高并发/SEO/边缘渲染诉求明显时优先 Next.js 15(React 19)Nuxt 3(Vue)
    • 数据密集型管理端推荐 AG Grid / TanStack Table 搭配 React Hook Form + Zod(或 Vue 对应方案)与 TanStack Query/SWR 做服务器状态管理。
    • 构建工具优先 Vite;需与 webpack 生态高度兼容且追求构建性能时评估 Rspack;微前端首选 Module Federationqiankun/single-spa 视存量系统而定。
    • 质量保障采用 Vitest + Testing Library(单测/组件)+ Playwright(E2E),将 a11y性能预算 纳入 CI。
    • 监控与可观测性:OpenTelemetry(Trace/Metrics/Logs 标准化) + Sentry(前端错误/性能)

2. 背景与业务约束

  • 典型场景:中后台表格/表单密集、数据可视化、权限/审计、国际化、多团队协作、持续交付。
  • 非功能需求:稳定性、可观测、可灰度、可回滚、性能(CWV 指标)、安全(XSS/依赖治理)。

3. 行业技术趋势【2024–2025】

  • 框架与渲染:React 19 稳定,Next.js 15 稳定。服务端组件(RSC)、Server Actions 与边缘渲染成为常态化能力。
  • Web 平台能力:CSS 容器查询选择器 :has()CSS Nesting 广泛可用;适配无栅格/卡片化布局更容易。
  • 体验指标INP 取代 FID 成为 Core Web Vitals 的交互性指标;与 LCP/CLS 一并成为常设目标。
  • 工程化:Vite 生态占据主流;Rspack 以 Rust 实现,强调 webpack 兼容 + 高性能;Monorepo 倾向 Turborepo/Nx + pnpm

4. 选型矩阵(按场景)

4.1 中后台(新建 SPA)

  • React 方案

    • UI:Ant Design 或 Arco Design
    • 表格:AG Grid(社区版足够)或 TanStack Table(无头 + 高可定制)
    • 表单:React Hook Form + Zod(类型安全校验)
    • 数据:TanStack Query 或 SWR(服务端状态)
    • 图表:Apache ECharts / AntV G2Plot
    • 国际化与无障碍:i18next、axe-core
    • 工具链:Vite、pnpm、ESLint+Prettier、Husky+lint-staged
    • 测试:Vitest + Testing Library(组件)+ Playwright(E2E)
    • 可观测性:OpenTelemetry + Sentry
  • Vue 方案

    • UI:Element Plus 或 Naive UI
    • 表格:AG Grid / TanStack Table(Vue 适配)
    • 表单:vee-validate 或 vue-hook-form(或配合 Zod)
    • 其余同 React 方案等价替换

4.2 SSR/边缘渲染/SEO

  • React:Next.js 15(App Router、RSC、Server Actions、Turbopack Dev)
  • Vue:Nuxt 3(服务器渲染、文件路由、数据获取)

4.3 微前端/多团队并行

  • 优先:Module Federation(Webpack/Rspack/Vite 插件)
  • 存量系统:qiankun(基于 single-spa)或直接使用 single-spa 作为内核
  • 适用:多技术栈共存、渐进改造、独立部署/灰度

4.4 数据可视化/大屏

  • ECharts 为主,必要时接入 AntV 系列(G2Plot/G6/X6)

5. 基础设施与工程体系

5.1 运行时与包管理

  • Node.js LTS(建议 20/22+),包管理优先 pnpm(工作区 + 硬链接节省磁盘)

5.2 Monorepo

  • TurborepoNx,开启远程缓存;配合 Changesets 统一版本与变更日志。

5.3 构建与开发体验

  • Vite(开发快、生态完备)
  • Rspack(兼容 webpack loaders/plugins,适合存量迁移或极致构建性能场景)
  • 产物:ESM 优先、可按需生成 modern/legacy 双产物(必要时)

5.4 CI/CD 基线

  • 质量门禁:typechecklinttesta11y 扫描、buildbundle size 比对、e2e 冒烟
  • 多环境:devstaging(含回归与金丝雀)→ prod;每个环境绑定独立监控指标与开关

6. 代码质量与测试策略

  • 测试金字塔

    • 单元/组件:Vitest + Testing Library(优先以“用户视角”查询 DOM)
    • 端到端:Playwright(多浏览器/移动端模拟)
    • 可访问性:axe-core(CI 内静态扫描 + 关键流程 E2E 断言)
  • 覆盖率目标:新增代码行覆盖 ≥ 80%,关键域模块函数覆盖 ≥ 90%

7. 性能与体验(Web Vitals 与预算)

  • 核心指标目标(P75,移动/桌面分段):

    • LCP ≤ 2.5s;INP ≤ 200ms;CLS ≤ 0.1
  • 性能预算(建议起点)

    • 首屏 JS(gzip)≤ 170–250 KB(按业务复杂度调整);首屏 CSS(gzip)≤ 50–100 KB
    • 关键路径图片采用 AVIF/WebP + 响应式源集;图标优先 SVG
  • 优化策略清单

    • RSC/Server Actions(SSR/边缘)减少客户端 JS;路由级/组件级代码拆分;prefetch/preload
    • 表格虚拟化、惰性渲染;避免长任务,使用 scheduler/requestIdleCallback;必要时 Web Worker/OffscreenCanvas
    • 数据层缓存与失效策略(Stale-While-Revalidate);合理设置 cache-control

8. 安全与合规

  • CSP(含 script-src 'nonce-...'strict-dynamic
  • Trusted Types(逐步开启/Report-Only → 强制)
  • 依赖治理:锁定 registry、启用 pnpm audit/SCA、禁止未备案三方脚本
  • XSS/CSRF/点击劫持/子资源完整性(SRI)基线配置

9. 可观测性与稳定性

  • OpenTelemetry JS:前端 Trace(与后端打通)、自定义事件、资源/网络指标
  • Sentry:错误聚合、来源映射(Source Map 上传)、性能事务
  • 关键流程埋点模型:登录/下单/导出/主操作路径;出问题可还原“用户故事”

10. 样例技术组合(可直接落地)

10.1 React 中后台(SPA)

  • Vite + React 19 + Ant Design + TanStack Query + RHF + Zod + ECharts + AG Grid + i18next + axe-core
  • Vitest + Testing Library + PlaywrightOpenTelemetry + Sentry

初始化建议

# 创建项目
pnpm create vite@latest acme-admin --template react-ts
cd acme-admin && pnpm i

# 关键依赖
pnpm add antd @tanstack/react-query react-hook-form zod i18next react-i18next axios echarts ag-grid-community ag-grid-react
pnpm add -D @testing-library/react @testing-library/user-event vitest playwright axe-core eslint prettier husky lint-staged @types/node

# 脚本建议
"scripts": {
  "dev": "vite",
  "build": "vite build",
  "preview": "vite preview",
  "typecheck": "tsc -b --pretty false",
  "lint": "eslint .",
  "test": "vitest run",
  "e2e": "playwright test"
}

10.2 SSR/边缘(React)

  • Next.js 15 + React 19 + Ant Design + TanStack Query(或使用 Server Actions)
  • 图片:Next Image;路由:App Router;部署:Vercel/自建边缘

10.3 Vue 中后台

  • Vite + Vue 3 + Element Plus/Naive UI + @tanstack/vue-query + vee-validate + Zod + ECharts + AG Grid

10.4 微前端

  • Module Federation(Webpack/Rspack/Vite 插件):分域部署、共享依赖版本策略(严格 SemVer + Exposes/Remotes 规划)
  • qiankun/single-spa:主/子应用路由隔离、样式沙箱、跨应用通信(全局状态/CustomEvent/URL)

11. 组织与治理

  • 编码规范:TypeScript、ESLint、Prettier、Commitlint(Conventional Commits)
  • 组件治理:Storybook(组件工作台)、设计令牌(Design Tokens)、样式主题化
  • ADR(Architecture Decision Record):重大技术决策留痕(背景→方案→取舍→影响→回滚计划)

12. 风险识别与应对

  • React 19/Next 15 升级风险:第三方库兼容性 → 先行 POC、灰度发布、双轨回滚
  • 微前端复杂度:跨应用通信与依赖地狱 → 规范共享依赖与版本、限制造成全局副作用的库
  • 构建链迁移(webpack → Vite/Rspack):自定义 loader/plugin 兼容性 → 梳理替代项与迁移脚本
  • 性能回退:上线后 CWV 恶化 → 设定性能闸门 + 观察期回滚机制

13. 路线图(90 天)

  • 0–2 周:需求与基线评估(现状盘点、性能与错误基线、依赖审计)
  • 3–4 周:关键路径 POC(React/Vue + 表格/表单/图表 + SSR/边缘是否必要)
  • 5–8 周:脚手架与工作流固化(Monorepo、CI/CD、质量门禁、模板仓库)
  • 9–12 周:试点应用迁移与灰度(设定 SLO:错误率、CWV、发布频率、MTTR)

14. 附录:库清单(按域)

  • UI:Ant Design、Arco Design、Element Plus、Naive UI
  • 表格:AG Grid、TanStack Table
  • 图表:Apache ECharts、AntV G2Plot
  • 数据获取/状态:Redux Toolkit(含 RTK Query)、TanStack Query、SWR
  • 表单/校验:React Hook Form、vee-validate、Zod、Yup
  • 构建:Vite、Rspack、Webpack、Module Federation(@module-federation/*)
  • 测试:Vitest、Testing Library、Playwright
  • 国际化/a11y:i18next、axe-core
  • 可观测性:OpenTelemetry JS、Sentry JS
  • 工程:pnpm、Turborepo、Nx、Changesets、Storybook

15. 参考与模板

本节给出可直接落地的脚手架、规范与 CI 模板,以及 Module Federation 最小仓库拓扑示意,并附常见领域模板片段。


15.1 脚手架命令(可复制)

React 中后台(Vite)

pnpm create vite@latest acme-admin --template react-ts
cd acme-admin && pnpm i

Next.js 15(SSR/边缘渲染)

pnpm dlx create-next-app@latest acme-web \
  --ts --eslint --app --src-dir --import-alias "@/*"
cd acme-web && pnpm i

Vue 3 中后台(Vite)

pnpm create vite@latest acme-console --template vue-ts
cd acme-console && pnpm i

Rspack(高性能打包 & 兼容 webpack 生态)

pnpm dlx create-rspack@latest acme-rspack
cd acme-rspack && pnpm i

Monorepo(Turborepo)

pnpm dlx create-turbo@latest acme-turbo -y
cd acme-turbo && pnpm i

Monorepo(Nx)

pnpm dlx create-nx-workspace@latest acme-nx --preset=apps --pm=pnpm
cd acme-nx && pnpm i

Vite + Module Federation(vite-plugin-federation)

pnpm add -D @originjs/vite-plugin-federation

15.2 代码规范与工程基线示例

tsconfig.base.json(Monorepo 可复用)

{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2022", "DOM"],
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "jsx": "react-jsx",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitOverride": true,
    "resolveJsonModule": true,
    "useDefineForClassFields": true,
    "skipLibCheck": true,
    "baseUrl": ".",
    "paths": {"@/*": ["src/*"]}
  }
}

.eslintrc.cjs(React/TS)

module.exports = {
  root: true,
  env: { browser: true, es2022: true, node: true },
  parser: "@typescript-eslint/parser",
  parserOptions: { ecmaVersion: "latest", sourceType: "module" },
  extends: [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:react/recommended",
    "plugin:react-hooks/recommended",
    "plugin:jsx-a11y/recommended",
    "plugin:import/recommended",
    "plugin:import/typescript",
    "prettier"
  ],
  settings: { react: { version: "detect" } },
  plugins: ["@typescript-eslint", "react", "react-hooks", "jsx-a11y", "import"],
  rules: {
    "import/order": ["warn", { "newlines-between": "always", "alphabetize": {"order": "asc"} }],
    "react/react-in-jsx-scope": "off",
    "@typescript-eslint/consistent-type-imports": ["warn", {prefer: "type-imports"}]
  }
}

.prettierrc

{ "singleQuote": true, "semi": false, "trailingComma": "all", "printWidth": 100 }

commitlint.config.cjs

module.exports = { extends: ["@commitlint/config-conventional"] }

package.json(lint-staged + Husky)

{
  "scripts": {
    "prepare": "husky",
    "lint": "eslint .",
    "format": "prettier -w .",
    "typecheck": "tsc -b --pretty false",
    "test": "vitest run"
  },
  "lint-staged": {
    "**/*.{ts,tsx,js,jsx}": ["eslint --fix"],
    "**/*.{json,md,css,scss}": ["prettier -w"]
  }
}

.editorconfig

root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

15.3 CI 样例

GitHub Actions:.github/workflows/ci.yml

name: CI
on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
jobs:
  build-and-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
        with: { version: 9 }
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'pnpm'
      - name: Install deps
        run: pnpm install --frozen-lockfile
      - name: Typecheck
        run: pnpm run typecheck
      - name: Lint
        run: pnpm run lint
      - name: Unit tests
        run: pnpm run test -- --coverage
      - name: Build
        run: pnpm run build
  e2e:
    needs: build-and-test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
        with: { version: 9 }
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'pnpm'
      - name: Install
        run: pnpm i
      - name: Install Playwright Browsers
        run: pnpm dlx playwright install --with-deps
      - name: E2E
        run: pnpm run e2e

GitLab CI:.gitlab-ci.yml

stages: [install, verify, build, e2e]
image: node:22-alpine
cache:
  key: ${CI_COMMIT_REF_SLUG}
  paths:
    - .pnpm-store
    - node_modules/
variables:
  PNPM_HOME: /root/.local/share/pnpm
  PLAYWRIGHT_BROWSERS_PATH: /ms-playwright
before_script:
  - corepack enable
  - pnpm -v
  - pnpm config set store-dir .pnpm-store

install:
  stage: install
  script: pnpm i --frozen-lockfile

verify:
  stage: verify
  script:
    - pnpm run typecheck
    - pnpm run lint
    - pnpm run test -- --coverage

build:
  stage: build
  script: pnpm run build
  artifacts:
    paths:
      - dist

e2e:
  stage: e2e
  image: mcr.microsoft.com/playwright:v1.47.0-jammy
  script:
    - pnpm i --frozen-lockfile
    - pnpm dlx playwright install --with-deps
    - pnpm run e2e

15.4 Module Federation 最小仓库拓扑与示例

目录拓扑(Monorepo)

root/
├─ apps/
│  ├─ shell/             # 宿主应用(路由/导航/统一布局)
│  └─ users/             # 远程应用(示例:用户域)
├─ packages/
│  └─ ui/                # 共享 UI 组件包
├─ tsconfig.base.json
├─ pnpm-workspace.yaml
└─ turbo.json | nx.json

宿主(webpack)apps/shell/webpack.config.js

const { ModuleFederationPlugin } = require('webpack').container
module.exports = {
  // ...entry/output/devServer
  plugins: [
    new ModuleFederationPlugin({
      name: 'shell',
      remotes: { users: 'users@http://localhost:4001/remoteEntry.js' },
      shared: { react: { singleton: true }, 'react-dom': { singleton: true } }
    })
  ]
}

远程(webpack)apps/users/webpack.config.js

const { ModuleFederationPlugin } = require('webpack').container
module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'users',
      filename: 'remoteEntry.js',
      exposes: { './UserCard': './src/UserCard.tsx' },
      shared: { react: { singleton: true }, 'react-dom': { singleton: true } }
    })
  ]
}

宿主使用远程

// apps/shell/src/App.tsx
// 声明模块:在 src/remote.d.ts 中加 declare module 'users/UserCard'
import UserCard from 'users/UserCard'
export default function App(){
  return <UserCard id="42" />
}

Vite 方案(vite-plugin-federation)

// apps/shell/vite.config.ts
import federation from '@originjs/vite-plugin-federation'
export default {
  plugins: [
    federation({
      name: 'shell',
      remotes: { users: 'http://localhost:4001/assets/remoteEntry.js' },
      shared: ['react', 'react-dom']
    })
  ]
}
// apps/users/vite.config.ts
import federation from '@originjs/vite-plugin-federation'
export default {
  plugins: [
    federation({
      name: 'users',
      filename: 'remoteEntry.js',
      exposes: { './UserCard': './src/UserCard.tsx' },
      shared: ['react', 'react-dom']
    })
  ]
}

启动端口约定

apps/shell:  http://localhost:4000
apps/users:  http://localhost:4001

15.5 版本与发布(Changesets)

安装与配置

pnpm add -D changesets
pnpm dlx changeset init

package.json 脚本

{
  "scripts": {
    "changeset": "changeset",
    "version-packages": "changeset version",
    "release": "pnpm build && changeset publish"
  }
}

GitHub Actions(自动发版) .github/workflows/release.yml

name: Release
on:
  push:
    branches: [ main ]
jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
        with: { version: 9 }
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          registry-url: 'https://registry.npmjs.org'
          cache: 'pnpm'
      - run: pnpm i --frozen-lockfile
      - run: pnpm run version-packages
      - run: pnpm run release
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

15.6 模板仓库结构(参考)

root/
├─ .github/
│  └─ workflows/ (ci.yml, release.yml)
├─ apps/ (shell, users, admin, web)
├─ packages/ (ui, config, eslint-config, tsconfig)
├─ e2e/ (Playwright 场景)
├─ .editorconfig  .prettierrc  .eslintrc.cjs  commitlint.config.cjs
├─ tsconfig.base.json  pnpm-workspace.yaml  turbo.json | nx.json
└─ README.md  ADRs/

15.7 安全与合规模板

CSP(Nginx 示例)

add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-__NONCE__' 'strict-dynamic'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; connect-src 'self' https://api.example.com; frame-ancestors 'none'; base-uri 'self'" always;

Trusted Types(Report-Only → 强制)

Content-Security-Policy: require-trusted-types-for 'script'; report-uri https://csp-report.example.com

富文本渲染安全(DOMPurify)

import DOMPurify from 'dompurify'
export function SafeHTML({ html }: { html: string }) {
  return <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(html) }} />
}

15.8 领域模板片段

网关鉴权(Axios 拦截器 + 刷新令牌)

import axios from 'axios'
let refreshing: Promise<string> | null = null
const api = axios.create({ baseURL: '/api' })
api.interceptors.request.use(cfg => {
  const token = localStorage.getItem('access_token')
  if (token) cfg.headers.Authorization = `Bearer ${token}`
  return cfg
})
api.interceptors.response.use(r => r, async (err) => {
  const { response, config } = err
  if (response?.status === 401 && !config._retried) {
    config._retried = true
    refreshing ||= fetch('/auth/refresh', { method: 'POST' }).then(r => r.text())
    const newToken = await refreshing.finally(() => (refreshing = null))
    localStorage.setItem('access_token', newToken)
    config.headers.Authorization = `Bearer ${newToken}`
    return api(config)
  }
  return Promise.reject(err)
})
export default api

权限路由(RBAC)

// 假设用户角色从会话获取
export function canAccess(roles: string[], need: string[]) {
  return need.every(r => roles.includes(r))
}
// React Router 示例
// <Route path="/admin" element={ canAccess(user.roles, ['admin']) ? <Admin/> : <NoAuth/> } />

CSV 导出(UTF-8 BOM 兼容 Excel)

export function exportCsv(name: string, rows: string[][]) {
  const csv = '' + rows.map(r => r.map(v => `"${(v ?? '').toString().replace(/"/g,'""')}"`).join(',')).join('
')
  const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' })
  const a = document.createElement('a')
  a.href = URL.createObjectURL(blob)
  a.download = `${name}.csv`
  a.click()
  URL.revokeObjectURL(a.href)
}

打印样式(隐藏非打印区)

@media print {
  .no-print { display: none !important; }
  body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
}

WYSIWYG 接入建议

  • 富文本编辑器:TipTap / Quill / TinyMCE。上传路径、粘贴过滤、图片压缩需统一策略。
  • 渲染端统一通过 DOMPurify 进行 sanitize;后端二次清洗 + 存储白名单。

低代码/可视化搭建接入(iframe + postMessage)

// 宿主监听
window.addEventListener('message', (e) => {
  if (e.origin !== 'https://builder.example.com') return
  const { type, payload } = e.data || {}
  if (type === 'SAVE_SCHEMA') {
    // TODO: 校验 + 存储
  }
})
// 子应用发送
window.parent.postMessage({ type: 'READY' }, '*')

添加新评论