3. Web 前端技术选型:React/Vue 框架对比、UI 组件库选择、状态管理方案
在自助容量压测作业平台中,Web 前端承担着任务编排、实时监控、报告展示等核心交互。选型需兼顾开发效率、生态成熟度、团队技术栈与长期维护成本。本章从框架、UI 组件库、状态管理三个维度进行技术选型分析。
3.1 框架选型:React vs Vue 3
React 与 Vue 3 均为当前主流前端框架,但在设计哲学、数据流、渲染机制上存在显著差异。以下从压测平台的关键需求出发进行对比:
| 对比维度 | React 18 | Vue 3 (Composition API) |
|---|---|---|
| 数据流 | 单向数据流,状态提升 + Context | 响应式代理 (Proxy),双向绑定可选 |
| 渲染机制 | 虚拟 DOM + Fiber 架构,并发模式 | 虚拟 DOM + 编译时优化 (PatchFlag) |
| 状态管理 | Redux / Zustand / Jotai 等第三方 | Pinia (官方推荐),Vuex 已退役 |
| TypeScript 支持 | 原生优秀,社区类型完善 | Vue 3 原生支持,但泛型推导略弱 |
| 学习曲线 | JSX 灵活,Hooks 规则需适应 | 模板语法直观,Composition API 渐进 |
| 生态成熟度 | 最庞大,第三方库覆盖全面 | 快速增长,国内社区活跃 |
| 压测平台适配性 | 适合复杂交互、大型仪表盘 | 适合表单密集、快速原型 |
选型建议: 若团队以 React 为主,优先选择 React 18 + TypeScript;若团队 Vue 经验更丰富,Vue 3 + Composition API 同样胜任。本课程后续示例以 React 18 展开,但设计思路可平移至 Vue 3。
3.2 UI 组件库选择
压测平台包含大量数据表格、表单、图表、拖拽编排等场景,UI 组件库需满足以下要求:
- 表格组件:支持虚拟滚动、列固定、行展开、批量操作
- 表单组件:动态表单项、校验联动、JSON Schema 渲染
- 图表组件:实时折线图、柱状图、热力图(TPS/响应时间/错误率)
- 拖拽组件:任务节点拖拽编排、DAG 流程图
- 主题定制:支持暗色模式、品牌色替换
| 组件库 | 框架 | 表格能力 | 拖拽支持 | 图表生态 | 主题定制 |
|---|---|---|---|---|---|
| Ant Design 5.x | React | ★★★★★ | 需配合 dnd-kit | @ant-design/charts | CSS-in-JS |
| Arco Design | React / Vue | ★★★★☆ | 内置拖拽排序 | BizCharts | Less 变量 |
| Element Plus | Vue 3 | ★★★★☆ | 需配合 vuedraggable | ECharts | CSS 变量 |
| Naive UI | Vue 3 | ★★★★☆ | 内置拖拽组件 | ECharts / 自定义 | 主题变量 |
推荐组合: React 技术栈选择 Ant Design 5.x + @ant-design/charts + @dnd-kit/core;Vue 3 技术栈选择 Naive UI + ECharts + vuedraggable@next。
3.3 状态管理方案
压测平台的状态管理需分层处理,避免全局状态膨胀。推荐采用 服务端状态 + 客户端状态 分离策略:
- 服务端状态:任务列表、压测报告、实时指标 —— 使用 React Query / TanStack Query 或 Vue Query 管理缓存与自动刷新
- 客户端状态:表单填写、UI 交互状态、拖拽编排 —— 使用 Zustand (React) 或 Pinia (Vue 3)
- URL 状态:筛选条件、分页页码、Tab 切换 —— 使用 React Router / Vue Router 的 query 参数
3.4 状态管理代码示例
以 React + Zustand 为例,展示压测任务编排的状态管理核心代码:
// store/taskStore.ts
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
interface TaskNode {
id: string;
type: 'http' | 'grpc' | 'websocket' | 'wait';
label: string;
config: Record<string, any>;
}
interface TaskEdge {
source: string;
target: string;
condition?: string;
}
interface TaskStore {
nodes: TaskNode[];
edges: TaskEdge[];
selectedNodeId: string | null;
// 操作
addNode: (node: TaskNode) => void;
removeNode: (id: string) => void;
updateNodeConfig: (id: string, config: Record<string, any>) => void;
setSelectedNode: (id: string | null) => void;
addEdge: (edge: TaskEdge) => void;
removeEdge: (source: string, target: string) => void;
}
export const useTaskStore = create<TaskStore>()(
devtools(
persist(
(set) => ({
nodes: [],
edges: [],
selectedNodeId: null,
addNode: (node) =>
set((state) => ({ nodes: [...state.nodes, node] })),
removeNode: (id) =>
set((state) => ({
nodes: state.nodes.filter((n) => n.id !== id),
edges: state.edges.filter(
(e) => e.source !== id && e.target !== id
),
})),
updateNodeConfig: (id, config) =>
set((state) => ({
nodes: state.nodes.map((n) =>
n.id === id ? { ...n, config: { ...n.config, ...config } } : n
),
})),
setSelectedNode: (id) => set({ selectedNodeId: id }),
addEdge: (edge) =>
set((state) => ({ edges: [...state.edges, edge] })),
removeEdge: (source, target) =>
set((state) => ({
edges: state.edges.filter(
(e) => !(e.source === source && e.target === target)
),
})),
}),
{
name: 'task-editor-storage',
partialize: (state) => ({
nodes: state.nodes,
edges: state.edges,
}),
}
)
)
);
避坑提示: 不要将所有状态放入全局 Store。实时压测指标(TPS、P99 延迟)应通过 WebSocket 推送直接更新图表组件,避免经过全局状态管理造成不必要的重渲染。推荐使用 React Query 的
useQuery 配合 WebSocket 订阅,或使用 Zustand 的 subscribeWithSelector 进行细粒度更新。
3.5 选型总结
| 层级 | React 技术栈 | Vue 3 技术栈 |
|---|---|---|
| 框架 | React 18 + TypeScript | Vue 3 + TypeScript |
| UI 组件库 | Ant Design 5.x | Naive UI |
| 图表 | @ant-design/charts (G2Plot) | ECharts 5 |
| 服务端状态 | TanStack Query (React Query) | TanStack Query (Vue Query) |
| 客户端状态 | Zustand | Pinia |
| 拖拽编排 | @dnd-kit/core + @dnd-kit/sortable | vuedraggable@next |
| 路由 | React Router 6 | Vue Router 4 |