核心哲学差异
| 方面 | Vue.js | React |
|---|---|---|
| 设计理念 | 渐进式框架 | 声明式UI库 |
| 核心思想 | "关爱开发者" | "拥抱函数式编程" |
| 学习曲线 | 平缓,渐进式 | 陡峭,概念较多 |
| 模板 vs JSX | 模板为主 | JSX 为主 |
1.架构设计差异
Vue - 渐进式框架
<!-- Vue 2/3 模板语法 --> <template> <div class="counter"> <h1>{{ title }}</h1> <p>Count: {{ count }}</p> <button @click="increment">+1</button> <ChildComponent :message="msg" @custom-event="handleEvent" /> </div> </template> <script> // 选项式 API (Vue 2/3 都支持) export default { data() { return { title: 'Vue Counter', count: 0, msg: 'Hello from parent' } }, methods: { increment() { this.count++ }, handleEvent(payload) { console.log(payload) } } } </script> <style scoped> .counter { color: blue; } </style>React - 纯粹的 UI 库
// React 函数组件 import { useState } from 'react'; import ChildComponent from './ChildComponent'; function Counter() { // Hooks 管理状态 const [count, setCount] = useState(0); const [title] = useState('React Counter'); // 事件处理 const increment = () => { setCount(count + 1); }; const handleEvent = (payload) => { console.log(payload); }; // JSX 语法 return ( <div className="counter"> <h1>{title}</h1> <p>Count: {count}</p> <button onClick={increment}>+1</button> <ChildComponent message="Hello from parent" onCustomEvent={handleEvent} /> </div> ); } // 内联样式 const styles = { counter: { color: 'blue' } };2.数据绑定方式
Vue - 双向数据绑定
<!-- Vue 2/3 --> <template> <!-- 双向绑定 v-model --> <input v-model="message" placeholder="输入内容"> <p>输入的内容: {{ message }}</p> <!-- 单向绑定 --> <input :value="message" @input="message = $event.target.value"> <!-- 计算属性 --> <p>反转消息: {{ reversedMessage }}</p> </template> <script> export default { data() { return { message: 'Hello Vue' } }, computed: { // 自动追踪依赖,缓存结果 reversedMessage() { return this.message.split('').reverse().join(''); } }, watch: { // 侦听器 message(newVal, oldVal) { console.log(`消息从 ${oldVal} 变为 ${newVal}`); } } } </script>React - 单向数据流
// React 单向数据流 function MessageComponent() { const [message, setMessage] = useState('Hello React'); // 手动处理双向绑定 const handleChange = (event) => { setMessage(event.target.value); }; // 计算值(每次渲染都计算) const reversedMessage = message.split('').reverse().join(''); // 使用 useEffect 监听变化 useEffect(() => { console.log(`消息变为: ${message}`); }, [message]); // 依赖数组 return ( <div> {/* 受控组件 */} <input value={message} onChange={handleChange} placeholder="输入内容" /> <p>输入的内容: {message}</p> <p>反转消息: {reversedMessage}</p> </div> ); }3.组件系统对比
Vue 组件系统
<!-- Vue 单文件组件 (SFC) --> <template> <div> <!-- 插槽系统 --> <header> <slot name="header">默认头部</slot> </header> <main> <slot>默认内容</slot> </main> <footer> <slot name="footer" :text="footerText"></slot> </footer> </div> </template> <script> export default { props: { // 详细的 prop 定义 title: { type: String, required: true, default: '默认标题', validator: (value) => value.length > 0 }, items: Array }, emits: ['update:title'], // 明确声明事件 data() { return { footerText: '页脚内容' } } } </script> <style scoped> /* 作用域 CSS */ div { padding: 20px; } </style>React 组件系统
// React 组件 import PropTypes from 'prop-types'; function Layout({ children, header, footer, renderFooter }) { return ( <div style={{ padding: '20px' }}> {/* Props 作为内容 */} <header>{header || '默认头部'}</header> <main>{children}</main> {/* Render Props 模式 */} <footer> {renderFooter ? renderFooter('页脚内容') : footer} </footer> </div> ); } // Prop 类型检查 Layout.propTypes = { title: PropTypes.string.isRequired, items: PropTypes.array, onUpdateTitle: PropTypes.func }; // 使用组件 <Layout header={<h1>自定义头部</h1>} footer="页脚文本" renderFooter={(text) => <p>{text}</p>} > <p>主要内容</p> </Layout>4.状态管理
Vue 状态管理 (Vuex/Pinia)
// Vue 3 + Pinia (现代方案) // stores/counter.js import { defineStore } from 'pinia'; export const useCounterStore = defineStore('counter', { state: () => ({ count: 0, user: null }), getters: { doubleCount: (state) => state.count * 2, // 带参数的计算属性 getUserById: (state) => (id) => { return state.users.find(user => user.id === id); } }, actions: { increment() { this.count++; }, async fetchUser(userId) { const user = await api.getUser(userId); this.user = user; } } }); // 组件中使用 <template> <div> <p>{{ store.count }}</p> <p>{{ store.doubleCount }}</p> <button @click="store.increment()">增加</button> </div> </template> <script setup> import { useCounterStore } from '@/stores/counter'; const store = useCounterStore(); </script>React 状态管理 (Redux/Context)
// React + Redux Toolkit (现代方案) // store/slices/counterSlice.js import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; export const fetchUser = createAsyncThunk( 'counter/fetchUser', async (userId) => { const response = await api.getUser(userId); return response.data; } ); const counterSlice = createSlice({ name: 'counter', initialState: { count: 0, user: null, status: 'idle' }, reducers: { increment: (state) => { state.count += 1; } }, extraReducers: (builder) => { builder .addCase(fetchUser.pending, (state) => { state.status = 'loading'; }) .addCase(fetchUser.fulfilled, (state, action) => { state.status = 'succeeded'; state.user = action.payload; }); } }); // 组件中使用 import { useSelector, useDispatch } from 'react-redux'; import { increment, fetchUser } from './counterSlice'; function Counter() { const count = useSelector((state) => state.counter.count); const dispatch = useDispatch(); return ( <div> <p>{count}</p> <button onClick={() => dispatch(increment())}>增加</button> <button onClick={() => dispatch(fetchUser(123))}>获取用户</button> </div> ); }5.生命周期与 Hooks
Vue 生命周期
<template> <div>{{ message }}</div> </template> <script> export default { data() { return { message: 'Hello' }; }, // 生命周期钩子 beforeCreate() { console.log('组件实例初始化之前'); }, created() { console.log('组件实例创建完成'); // 可以访问 data、methods,但 DOM 未挂载 }, beforeMount() { console.log('挂载前'); }, mounted() { console.log('挂载完成'); // 可以访问 DOM }, beforeUpdate() { console.log('更新前'); }, updated() { console.log('更新完成'); }, beforeUnmount() { console.log('卸载前'); }, unmounted() { console.log('已卸载'); } }; </script> <!-- Vue 3 Composition API --> <script setup> import { onMounted, onUnmounted, ref } from 'vue'; const message = ref('Hello'); // 组合式 API onMounted(() => { console.log('组件挂载'); // 执行初始化操作 }); onUnmounted(() => { console.log('组件卸载'); // 清理操作 }); </script>React 生命周期 (函数组件 + Hooks)
import { useState, useEffect, useMemo, useCallback } from 'react'; function Example() { const [count, setCount] = useState(0); const [data, setData] = useState(null); // 相当于 componentDidMount + componentDidUpdate useEffect(() => { console.log('组件挂载或count变化'); // 清理函数,相当于 componentWillUnmount return () => { console.log('清理操作'); }; }, [count]); // 依赖数组 // 只执行一次(挂载时) useEffect(() => { console.log('组件挂载'); fetchData(); }, []); // 空依赖数组 // 相当于 computed const doubledCount = useMemo(() => { return count * 2; }, [count]); // 缓存函数,避免不必要的重新渲染 const increment = useCallback(() => { setCount(c => c + 1); }, []); const fetchData = async () => { const result = await api.getData(); setData(result); }; return ( <div> <p>Count: {count}, Doubled: {doubledCount}</p> <button onClick={increment}>增加</button> </div> ); }6.响应式系统实现
Vue 响应式原理
// Vue 3 响应式系统 import { reactive, ref, watch, computed } from 'vue'; // reactive: 对象响应式 const state = reactive({ count: 0, user: { name: 'John', age: 25 } }); // 嵌套对象也是响应式的 state.user.age++; // 触发更新 // ref: 基本类型响应式 const count = ref(0); count.value++; // 通过 .value 访问 // 计算属性 const doubleCount = computed(() => state.count * 2); // 侦听器 watch( () => state.count, (newVal, oldVal) => { console.log(`Count changed: ${oldVal} -> ${newVal}`); }, { immediate: true, deep: true } ); // 自动依赖收集 const effect = () => { console.log(`Count is: ${state.count}`); }; // state.count 变化时,effect 自动重新执行React 响应式原理
// React 的响应式是基于状态变化的重新渲染 function Component() { const [state, setState] = useState({ count: 0, user: { name: 'John', age: 25 } }); // 更新状态(浅比较) const increment = () => { setState(prev => ({ ...prev, count: prev.count + 1 })); }; // 更新嵌套对象需要手动处理 const updateUserAge = () => { setState(prev => ({ ...prev, user: { ...prev.user, age: prev.user.age + 1 } })); }; // 不会自动追踪依赖 // 每次渲染都会执行 const doubleCount = state.count * 2; return ( <div> <p>Count: {state.count}</p> <p>Double: {doubleCount}</p> <button onClick={increment}>增加</button> </div> ); }7.生态系统和工具链
Vue 生态系统
Vue 生态系统 ├── 官方路由: Vue Router ├── 状态管理: │ ├── Vuex (官方, Vue 2) │ └── Pinia (推荐, Vue 3) ├── 服务端渲染: Nuxt.js ├── 移动端: │ ├── Ionic Vue │ └── Quasar ├── 桌面端: Electron + Vue ├── 测试: │ ├── Vue Test Utils │ └── Vitest (推荐) ├── 构建工具: Vite (官方推荐) └── UI 框架: ├── Element Plus ├── Vuetify ├── Ant Design Vue └── PrimeVueReact 生态系统
React 生态系统 ├── 官方路由: React Router ├── 状态管理: │ ├── Redux (最流行) │ ├── MobX │ ├── Recoil (Facebook) │ └── Zustand (轻量级) ├── 服务端渲染: │ ├── Next.js (最流行) │ └── Remix ├── 移动端: React Native ├── 桌面端: Electron + React ├── 测试: │ ├── Jest │ ├── React Testing Library │ └── Cypress (E2E) ├── 构建工具: │ ├── Create React App (官方) │ ├── Vite │ └── Next.js (内置) └── UI 框架: ├── Material-UI (MUI) ├── Ant Design ├── Chakra UI └── Tailwind CSS + Headless UI8.性能优化
Vue 性能优化
<template> <!-- 列表渲染优化 --> <div v-for="item in list" :key="item.id"> {{ item.name }} </div> <!-- 计算属性缓存 --> <p>{{ expensiveComputation }}</p> <!-- 组件懒加载 --> <template v-if="showComponent"> <AsyncComponent /> </template> <!-- 条件渲染优化 --> <template v-if="isVisible"> <HeavyComponent /> </template> </template> <script> import { defineAsyncComponent } from 'vue'; // 异步组件 const AsyncComponent = defineAsyncComponent(() => import('./AsyncComponent.vue') ); export default { computed: { // 缓存计算结果 expensiveComputation() { // 复杂计算,只有依赖变化时重新计算 return this.data.reduce((sum, item) => sum + item.value, 0); } }, // 组件懒加载 components: { AsyncComponent } }; </script>React 性能优化
import React, { memo, useMemo, useCallback, lazy, Suspense } from 'react'; // 1. React.memo 避免不必要的重新渲染 const ChildComponent = memo(function ChildComponent({ data, onClick }) { return <button onClick={onClick}>{data}</button>; }); // 2. useMemo 缓存计算结果 function ExpensiveComponent({ list }) { const total = useMemo(() => { return list.reduce((sum, item) => sum + item.value, 0); }, [list]); // 只有 list 变化时重新计算 return <div>{total}</div>; } // 3. useCallback 缓存函数 function ParentComponent() { const handleClick = useCallback(() => { console.log('Clicked'); }, []); // 空依赖,函数不会重新创建 return <ChildComponent onClick={handleClick} />; } // 4. 代码分割和懒加载 const LazyComponent = lazy(() => import('./LazyComponent')); function App() { return ( <Suspense fallback={<div>Loading...</div>}> <LazyComponent /> </Suspense> ); } // 5. 虚拟列表 (react-window 或 react-virtualized) import { FixedSizeList } from 'react-window'; function VirtualList() { return ( <FixedSizeList height={400} itemCount={1000} itemSize={50}> {({ index, style }) => ( <div style={style}>Item {index}</div> )} </FixedSizeList> ); }9.TypeScript 支持
Vue + TypeScript
<!-- Vue 3 + TypeScript --> <template> <div> <p>{{ user.name }}</p> <button @click="updateUser">更新</button> </div> </template> <script setup lang="ts"> import { ref } from 'vue'; // 类型定义 interface User { id: number; name: string; email: string; age?: number; } // 响应式数据 const user = ref<User>({ id: 1, name: 'John', email: 'john@example.com' }); // 事件处理 const updateUser = (): void => { user.value.name = 'Jane'; }; // Props 类型定义 defineProps<{ title: string; count: number; }>(); // Emits 类型定义 const emit = defineEmits<{ (e: 'update', value: number): void; }>(); </script>React + TypeScript
// React + TypeScript import React, { useState } from 'react'; // 类型定义 interface User { id: number; name: string; email: string; age?: number; } interface UserCardProps { user: User; onUpdate: (user: User) => void; showDetails?: boolean; } // 函数组件 const UserCard: React.FC<UserCardProps> = ({ user, onUpdate, showDetails = false }) => { const [count, setCount] = useState<number>(0); const handleClick = (): void => { setCount(prev => prev + 1); onUpdate({ ...user, name: `Updated ${user.name}` }); }; return ( <div> <h3>{user.name}</h3> {showDetails && <p>Email: {user.email}</p>} <p>点击次数: {count}</p> <button onClick={handleClick}>更新</button> </div> ); }; // 泛型组件 function List<T>({ items, renderItem }: { items: T[]; renderItem: (item: T) => React.ReactNode; }) { return ( <ul> {items.map((item, index) => ( <li key={index}>{renderItem(item)}</li> ))} </ul> ); }选择建议
选择 Vue 的场景:
初学者友好:文档优秀,学习曲线平缓
快速原型:双向绑定减少样板代码
小型到中型项目:内置功能齐全
传统团队:更接近传统 Web 开发思维
需要清晰结构:SFC 组织方式明确
选择 React 的场景:
大型复杂应用:更好的可预测性
需要高度灵活:JSX 表达能力更强
跨平台需求:React Native 生态
函数式编程偏好:Hooks 模式
企业级应用:更成熟的测试和工具链
两者都合适的场景:
中大型 SPA:两者都有良好支持
需要 SSR:Nuxt.js (Vue) 和 Next.js (React)
TypeScript 项目:两者都有优秀支持
移动端 PWA:都有成熟方案
发展趋势
Vue 3 的优势:
Composition API 更灵活
更好的 TypeScript 支持
更小的包体积
Vite 构建工具极速体验
React 18 的优势:
并发特性 (Concurrent Mode)
服务端组件 (Server Components)
自动批处理优化
更稳定的生态系统
总结
| 维度 | Vue | React |
|---|---|---|
| 上手难度 | ⭐⭐☆☆☆ | ⭐⭐⭐☆☆ |
| 开发速度 | ⭐⭐⭐⭐☆ | ⭐⭐⭐☆☆ |
| 灵活性 | ⭐⭐⭐☆☆ | ⭐⭐⭐⭐⭐ |
| 性能 | ⭐⭐⭐⭐☆ | ⭐⭐⭐⭐☆ |
| 生态系统 | ⭐⭐⭐☆☆ | ⭐⭐⭐⭐⭐ |
| TypeScript支持 | ⭐⭐⭐⭐☆ | ⭐⭐⭐⭐⭐ |
| 移动端 | ⭐⭐☆☆☆ | ⭐⭐⭐⭐⭐ |
| 就业市场 | ⭐⭐⭐☆☆ | ⭐⭐⭐⭐⭐ |
最终建议:
如果是新手或个人项目,从 Vue 开始更容易
如果是大型企业项目或团队,React 可能更合适
考虑团队现有技术栈和技能
两者都是优秀的选择,掌握核心概念后相互转换不难