Vue 与 React 对比
Vue 和 React 是目前最流行的两大前端框架,很多人在学习或选择时会纠结。本文将从几个核心方面进行对比,帮助你快速理解两者的差异。
概述
| 特性 | Vue | React |
|---|---|---|
| 创立时间 | 2014 年 | 2013 年 |
| 作者 | 尤雨溪 | |
| 学习曲线 | 较平缓 | 较陡峭 |
| 社区生态 | 丰富 | 非常丰富 |
| 核心理念 | 渐进式、易上手 | 函数式、灵活 |
组件传参对比
父传子
Vue 使用 props
vue
<!-- Parent.vue -->
<template>
<ChildComponent message="Hello Vue" :count="10" />
</template>
<script setup>
import ChildComponent from './ChildComponent.vue'
</script>
<!-- Child.vue -->
<template>
<div>
<p>{{ message }}</p>
<p>Count: {{ count }}</p>
</div>
</template>
<script setup>
defineProps({
message: String,
count: {
type: Number,
default: 0
}
})
</script>React 使用 props
jsx
// Parent.jsx
function Parent() {
return <ChildComponent message="Hello React" count={10} />
}
// Child.jsx
function Child({ message, count = 0 }) {
return (
<div>
<p>{message}</p>
<p>Count: {count}</p>
</div>
)
}子传父
Vue 使用 emit
vue
<!-- Child.vue -->
<template>
<button @click="handleClick">点击发送数据</button>
</template>
<script setup>
const emit = defineEmits(['send-data'])
function handleClick() {
emit('send-data', '来自子组件的数据')
}
</script>
<!-- Parent.vue -->
<template>
<ChildComponent @send-data="handleReceived" />
</template>
<script setup>
function handleReceived(data) {
console.log('收到数据:', data)
}
</script>React 使用回调函数
jsx
// Child.jsx
function Child({ onSend }) {
function handleClick() {
onSend('来自子组件的数据')
}
return <button onClick={handleClick}>点击发送数据</button>
}
// Parent.jsx
function Parent() {
function handleReceived(data) {
console.log('收到数据:', data)
}
return <Child onSend={handleReceived} />
}兄弟组件通信
Vue 使用事件总线或状态管理
javascript
// eventBus.js
import { mitt } from 'mitt'
const eventBus = mitt()
// ComponentA.vue
<script setup>
import eventBus from './eventBus.js'
function sendMessage() {
eventBus.emit('message', '来自组件A')
}
</script>
// ComponentB.vue
<script setup>
import { onMounted, onUnmounted } from 'vue'
import eventBus from './eventBus.js'
onMounted(() => {
eventBus.on('message', (data) => {
console.log('收到消息:', data)
})
})
onUnmounted(() => {
eventBus.off('message')
})
</script>React 使用状态提升或事件总线
jsx
// 使用状态提升到父组件
function Parent() {
const [message, setMessage] = useState('')
return (
<>
<ComponentA onMessage={setMessage} />
<ComponentB message={message} />
</>
)
}路由对比
Vue Router
javascript
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from './views/Home.vue'
import About from './views/About.vue'
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About },
{ path: '/user/:id', component: User } // 动态路由
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
<!-- App.vue -->
<template>
<router-link to="/">首页</router-link>
<router-link to="/about">关于</router-link>
<router-view />
</template>
<!-- 组件中跳转 -->
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
function goToUser(id) {
router.push('/user/' + id)
}
</script>React Router
jsx
// App.jsx
import { BrowserRouter, Routes, Route, Link, useNavigate } from 'react-router-dom'
function App() {
return (
<BrowserRouter>
<nav>
<Link to="/">首页</Link>
<Link to="/about">关于</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/user/:id" element={<User />} />
</Routes>
</BrowserRouter>
)
}
// 组件中跳转
function User() {
const navigate = useNavigate()
function goToHome() {
navigate('/')
}
return <button onClick={goToHome}>返回首页</button>
}全局状态管理对比
Vue 使用 Pinia(推荐)或 Vuex
Pinia 示例
javascript
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
name: '计数器'
}),
getters: {
doubleCount: (state) => state.count * 2
},
actions: {
increment() {
this.count++
},
decrement() {
this.count--
}
}
})
<!-- 组件中使用 -->
<script setup>
import { useCounterStore } from '../stores/counter'
const store = useCounterStore()
// 直接使用
console.log(store.count)
store.increment()
// 解构使用(保持响应式)
import { storeToRefs } from 'pinia'
const { count, doubleCount } = storeToRefs(store)
</script>
<template>
<p>{{ store.count }}</p>
<p>双倍: {{ store.doubleCount }}</p>
<button @click="store.increment">+1</button>
</template>Vuex 示例
javascript
// stores/index.js
import { createStore } from 'vuex'
export default createStore({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++
}
},
actions: {
asyncIncrement({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
},
getters: {
doubleCount: state => state.count * 2
}
})
// 组件中使用
this.$store.state.count
this.$store.commit('increment')React 使用 Redux 或 Zustand
Zustand 示例(更简洁)
javascript
// stores/useCounterStore.js
import { create } from 'zustand'
const useCounterStore = create((set) => ({
count: 0,
name: '计数器',
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 })
}))
export default useCounterStore
// 组件中使用
import useCounterStore from '../stores/useCounterStore'
function Counter() {
const { count, increment } = useCounterStore()
return (
<div>
<p>{count}</p>
<button onClick={increment}>+1</button>
</div>
)
}Redux Toolkit 示例
javascript
// features/counter/counterSlice.js
import { createSlice, configureStore } from '@reduxjs/toolkit'
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: (state) => { state.value += 1 },
decrement: (state) => { state.value -= 1 }
}
})
export const { increment, decrement } = counterSlice.actions
export default counterSlice.reducer
// store.js
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from './counterSlice'
export const store = configureStore({
reducer: {
counter: counterReducer
}
})
// 组件中使用
import { useDispatch, useSelector } from 'react-redux'
import { increment, decrement } from '../counterSlice'
function Counter() {
const dispatch = useDispatch()
const count = useSelector(state => state.counter.value)
return (
<div>
<p>{count}</p>
<button onClick={() => dispatch(increment())}>+1</button>
<button onClick={() => dispatch(decrement())}>-1</button>
</div>
)
}模板语法对比
Vue 使用 HTML 模板
vue
<template>
<div class="container">
<h1>{{ title }}</h1>
<ul>
<li v-for="item in items" :key="item.id">
{{ item.name }}
</li>
</ul>
<button @click="handleClick">点击</button>
</div>
</template>React 使用 JSX
jsx
function MyComponent({ title, items }) {
function handleClick() {
console.log('点击')
}
return (
<div className="container">
<h1>{title}</h1>
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
<button onClick={handleClick}>点击</button>
</div>
)
}数据响应式对比
Vue 自动响应式
vue
<script setup>
import { ref, reactive, computed } from 'vue'
// 简单类型用 ref
const count = ref(0)
// 复杂类型用 reactive
const state = reactive({
name: 'Vue',
items: []
})
// 计算属性
const doubleCount = computed(() => count.value * 2)
// 修改值
count.value++
// 或者
state.name = '新名称'
</script>React 需要手动触发更新
jsx
import { useState, useReducer, useMemo } from 'react'
function Counter() {
// useState 返回状态和更新函数
const [count, setCount] = useState(0)
// 复杂状态用 useReducer
const [state, dispatch] = useReducer(reducer, initialState)
// 计算属性用 useMemo
const doubleCount = useMemo(() => count * 2, [count])
// 修改值
setCount(count + 1)
}选择建议
| 场景 | 推荐 | 原因 |
|---|---|---|
| 快速上手项目 | Vue | 学习曲线平缓,文档友好 |
| 追求灵活性 | React | 自由度更高,生态更广 |
| 中小型项目 | Vue | 简单场景下更便捷 |
| 大型复杂项目 | React | 更适合大型团队协作 |
| 团队已有经验 | 根据团队技术栈 | 降低学习成本 |
总结
Vue 和 React 都是优秀的框架,没有绝对的好坏之分:
- Vue:更注重开发体验,上手简单,适合快速开发
- React:更注重灵活性和函数式编程,适合大型复杂项目
选择哪个取决于你的项目需求、团队背景和个人喜好。最重要的是掌握核心概念,这样才能在两个框架之间灵活切换。
持续更新中...