从真实项目说起

当时我们面临的是一个实时交易监控系统,需要处理高频数据更新和复杂的交互状态。团队里有三个前端,两个是React背景,一个是Vue背景。我们做了个大胆的决定:用两个星期分别用React和Vue实现同一个核心模块——实时持仓看板。

代码对比:状态管理

​React版本(当时用的是Class组件)​​:

class PositionMonitor extends Component {
  constructor(props) {
    super(props);
    this.state = {
      positions: [],
      sortField: 'profit',
      filter: {}
    };
    this.socket = null;
  }

  componentDidMount() {
    this.socket = new WebSocket(API_URL);
    this.socket.onmessage = (event) => {
      const data = JSON.parse(event.data);
      this.setState(prevState => ({
        positions: this.mergePositions(prevState.positions, data)
      }));
    };
  }

  mergePositions = (existing, updates) => {
    // 复杂的实时数据合并逻辑
    return existing.map(pos => {
      const update = updates.find(u => u.id === pos.id);
      return update ? { ...pos, ...update } : pos;
    });
  }

  handleSort = (field) => {
    this.setState({ sortField: field });
  }

  render() {
    const sorted = this.sortPositions(this.state.positions);
    return (
      <div>
        <SortControls onSort={this.handleSort} />
        <PositionList positions={sorted} />
      </div>
    );
  }
}

​Vue版本​:

<template>
  <div>
    <SortControls @sort-change="handleSort" />
    <PositionList :positions="sortedPositions" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      positions: [],
      sortField: 'profit',
      filter: {}
    }
  },
  computed: {
    sortedPositions() {
      return this.positions.sort((a, b) => 
        a[this.sortField] - b[this.sortField]
      );
    }
  },
  mounted() {
    this.socket = new WebSocket(API_URL);
    this.socket.onmessage = (event) => {
      const data = JSON.parse(event.data);
      this.positions = this.mergePositions(this.positions, data);
    };
  },
  methods: {
    handleSort(field) {
      this.sortField = field;
    },
    mergePositions(existing, updates) {
      // 同样的合并逻辑
      return existing.map(pos => {
        const update = updates.find(u => u.id === pos.id);
        return update ? { ...pos, ...update } : pos;
      });
    }
  }
}
</script>

两周后的代码评审会上,我们发现了一些有趣的现象:

技术决策的思考过程

1. 学习曲线:Vue确实更友好

团队里那个Vue背景的同事两天就上手了React,但React背景的同事看Vue的模板语法却有点懵。Vue的单文件组件把HTML、CSS、JS放在一起,更符合传统Web开发者的思维习惯。

​但这里有个陷阱​:简单的项目Vue上手快,但复杂项目里React的显式数据流反而更容易调试。就像我们后来遇到的一个bug,Vue的响应式系统在深层对象更新时行为不太直观,而React的setState虽然啰嗦,但行为可预测。

2. 性能:其实差别没有想象中大

我们做了性能测试,在1000行数据的表格渲染上:

  • React: 初次渲染 120ms,更新 45ms
  • Vue: 初次渲染 110ms,更新 38ms

差别微乎其微。​真正的性能瓶颈在于我们自己的代码,比如那个mergePositions函数,优化后性能提升了3倍。

3. 生态系统:React赢了规模,Vue赢了体验

React的生态确实更丰富,但这也意味着选择成本更高。我们需要在几十个状态管理库中选择(最后选了Zustand,因为Redux太重了)。Vue的官方生态更一体化,Vuex、Vue Router都是官方维护,决策成本低。

2023年的新认知

五年过去了,两个框架都发生了很大变化:

Hooks改变了React

// 现代React代码
function PositionMonitor() {
  const [positions, setPositions] = useState([]);
  const [sortField, setSortField] = useState('profit');
  
  const sortedPositions = useMemo(() => {
    return [...positions].sort((a, b) => a[sortField] - b[sortField]);
  }, [positions, sortField]);

  useEffect(() => {
    const socket = new WebSocket(API_URL);
    socket.onmessage = (event) => {
      const data = JSON.parse(event.data);
      setPositions(prev => mergePositions(prev, data));
    };
    return () => socket.close();
  }, []);

  return (
    <div>
      <SortControls onSort={setSortField} />
      <PositionList positions={sortedPositions} />
    </div>
  );
}

Hooks让React代码更简洁,但需要理解闭包陷阱和依赖数组。我们团队花了两个月才完全适应,期间踩了不少坑。

Vue 3的Composition API

<template>
  <div>
    <SortControls @sort-change="setSortField" />
    <PositionList :positions="sortedPositions" />
  </div>
</template>

<script setup>
import { ref, computed, onMounted } from 'vue';

const positions = ref([]);
const sortField = ref('profit');

const sortedPositions = computed(() => {
  return positions.value.sort((a, b) => a[sortField.value] - b[sortField.value]);
});

onMounted(() => {
  const socket = new WebSocket(API_URL);
  socket.onmessage = (event) => {
    const data = JSON.parse(event.data);
    positions.value = mergePositions(positions.value, data);
  };
});
</script>

Vue 3的Composition API让代码组织更灵活,但失去了原来选项式API的清晰结构。我们有些老项目迁移时遇到了类型推断的问题。

真实项目中的痛点对比

状态管理复杂度

​React + Zustand:

// store/counter.js
const useCounter = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}));

// 组件使用
function Counter() {
  const { count, increment } = useCounter();
  return <button onClick={increment}>{count}</button>;
}

​Vue + Pinia:

// stores/counter.js
export const useCounter = defineStore('counter', () => {
  const count = ref(0);
  function increment() {
    count.value++;
  }
  return { count, increment };
});

// 组件使用
const counter = useCounter();

React的状态管理更自由但需要更多决策,Vue的更标准化但有时不够灵活。

TypeScript支持

React的TS支持确实更好,特别是泛型组件:

interface ListProps<T> {
  items: T[];
  renderItem: (item: T) => ReactNode;
}

function List<T>({ items, renderItem }: ListProps<T>) {
  return <div>{items.map(renderItem)}</div>;
}

Vue的模板内类型检查还在完善中,但Script Setup+TypeScript已经很好用了。

我的选择标准

经过这么多项目,我现在这样选择:

选择React当:

  • 团队有React经验
  • 项目复杂度高,需要自定义抽象
  • 需要服务端渲染(Next.js生态更成熟)
  • 团队喜欢函数式编程

选择Vue当:

  • 团队前端经验较浅
  • 项目需要快速上线
  • 需要更好的开发体验(Vue Devtools很棒)
  • 项目规模中等,不需要太复杂的抽象

一些反常识的发现

  1. ​文档质量​:Vue的文档确实更好,但React的社区解答更丰富
  2. ​招聘难度​:React程序员更多,但Vue程序员更稳定(个人观察)
  3. ​长期维护​:Vue 2到3的迁移比React 15到16痛苦得多
  4. ​移动端​:React Native确实是个优势,但现在Vue也有了不错的移动方案

给团队的技术建议

​如果是新项目​:考虑团队背景,两个都是好选择。我更倾向于React,因为它的思维模型更通用,学会了React再学Vue很容易,反过来则不然。

​如果是老项目重构​:用团队熟悉的框架。重写成本远高于框架差异带来的收益。

​如果是创业公司​:Vue可能更适合,开发速度更快,招初级程序员成本更低。

框架选择就像选结婚对象,没有最好,只有最合适。我们团队现在两个框架都在用,重要的是理解它们的设计哲学,而不是死磕技术细节。

最后分享一个真实数据:我们最近一年维护的6个项目中,4个用React,2个用Vue,项目成功率都是100%。​框架本身不决定项目成败,团队的技术能力和工程实践才是关键。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐