从vue技术栈快速转到react技术栈
从 Vue 转到 React 并不难,两者作为主流前端框架,核心思想(组件化、声明式编程)一致,但在语法、API 设计和最佳实践上存在差异。下面从核心概念、语法差异、关键特性到实战技巧,帮你快速上手 React。
一、核心思想对比:理解「差异根源」
| 维度 | Vue | React |
|---|---|---|
| 设计理念 | 渐进式框架,强调「模板与逻辑分离」 | 函数式理念,强调「UI = 函数(状态)」,逻辑与 UI 通过 JS 结合 |
| 核心范式 | 基于「模板语法 + 选项式 API」(Vue 2)或「组合式 API」(Vue 3) | 基于「JSX + 函数组件 + Hooks」,一切皆 JavaScript |
| 状态驱动 | 双向绑定(v-model)+ 响应式系统自动追踪依赖 | 单向数据流,通过「状态更新触发重新渲染」实现响应式 |
| 灵活性 | 内置解决方案更完善(如模板指令、scoped 样式) | 更灵活,需自行选择生态工具(如样式方案、状态库) |
二、语法差异:从「模板」到「JSX」
Vue 用 template 模板语法,React 用 JSX(JavaScript XML),这是最直观的差异。
1. 组件定义:从 .vue 到 JSX 组件
Vue 的单文件组件(SFC)拆分了 template、script、style,而 React 组件直接在 JS/TS 文件中用函数返回 JSX。
Vue 组件(Vue 3 组合式 API):
<!-- Hello.vue -->
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<button @click="handleClick">点击</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const msg = ref('Hello Vue')
const handleClick = () => {
msg.value = '点击后的文本'
}
</script>
<style scoped>
.hello { color: red; }
</style>
React 组件(函数组件):
// Hello.jsx
import { useState } from 'react';
import './Hello.css'; // 样式单独引入
const Hello = () => {
// 状态定义(替代 Vue 的 ref)
const [msg, setMsg] = useState('Hello React');
// 事件处理(替代 @click)
const handleClick = () => {
setMsg('点击后的文本'); // 状态更新(React 需显式调用更新函数)
};
// JSX 直接返回(替代 template)
return (
<div className="hello"> {/* class → className(避免与 JS 关键字冲突) */}
<h1>{msg}</h1> {/* 插值用 { } 替代 {{ }} */}
<button onClick={handleClick}>点击</button> {/* @click → onClick(驼峰命名) */}
</div>
);
};
export default Hello;
2. JSX vs Vue Template:语法细节对比
| 功能 | Vue Template | React JSX |
|---|---|---|
| 插值 | {{ message }} | { message } |
| 条件渲染 | v-if="isShow" / v-else | { isShow ? <div>显示</div> : <div>隐藏</div> } 或 { isShow && <div>显示</div> } |
| 列表渲染 | v-for="(item, index) in list" :key="index" | list.map((item, index) => <div key={index}>{item}</div>) |
| 事件绑定 | @click="handleClick" / @input="handleInput" | onClick={handleClick} / onChange={handleInput}(驼峰命名) |
| 属性绑定 | :src="imgUrl" / :class="{ active: isActive }" | src={imgUrl} / className={isActive ? 'active' : ''} |
| 样式绑定 | :style="{ color: textColor }" | style={{ color: textColor }}(双层大括号:外层 JSX,内层对象) |
三、核心特性对比:状态、生命周期、事件
1. 状态管理:从 ref/reactive 到 useState/useReducer
Vue 用 ref/reactive 定义响应式状态,React 用 useState(简单状态)或 useReducer(复杂状态)。
| 场景 | Vue 3 | React |
|---|---|---|
| 简单状态 | const count = ref(0); count.value++ | const [count, setCount] = useState(0); setCount(prev => prev + 1) |
| 对象状态 | const user = reactive({ name: 'xx' }); user.name = 'yy' | const [user, setUser] = useState({ name: 'xx' }); setUser(prev => ({ ...prev, name: 'yy' }))(需「不可变更新」) |
| 复杂状态逻辑 | Pinia store(actions 处理逻辑) | useReducer(类似 Vuex 的 reducer)或 Redux |
关键区别:
React 状态更新是「不可变的」(不能直接修改原状态,需返回新对象),而 Vue 可以直接修改响应式对象(内部通过 Proxy 追踪变化)。
2. 生命周期:从 mounted 到 useEffect
Vue 的生命周期钩子(如 mounted、updated)在 React 中统一用 useEffect 模拟,更灵活但需理解依赖逻辑。
| Vue 生命周期 | React useEffect 实现 |
|---|---|
created + mounted | useEffect(() => { /* 组件挂载后执行 */ }, []);(空依赖数组 → 只执行一次) |
updated(监听某个状态) | useEffect(() => { /* 依赖变化时执行 */ }, [count]);(依赖数组含 count → count 变则执行) |
beforeUnmount | useEffect(() => { return () => { /* 组件卸载前清理 */ }; }, []);(返回清理函数) |
示例:监听窗口大小(Vue vs React)
Vue:
<script setup>
import { onMounted, onUnmounted } from 'vue';
let width = 0;
const handleResize = () => { width = window.innerWidth; };
onMounted(() => window.addEventListener('resize', handleResize));
onUnmounted(() => window.removeEventListener('resize', handleResize));
</script>
React:
import { useState, useEffect } from 'react';
const ResizeDemo = () => {
const [width, setWidth] = useState(0);
// 监听窗口大小(mounted 时绑定,unmounted 时解绑)
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
// 清理函数(替代 beforeUnmount)
return () => window.removeEventListener('resize', handleResize);
}, []); // 空依赖 → 只执行一次
return <div>窗口宽度:{width}</div>;
};
3. 事件处理:从 @click 到 onClick
- 命名规则:Vue 用短横线(
@click、@input),React 用驼峰(onClick、onChange)。 - 事件对象:Vue 用
$event,React 直接接收事件对象(合成事件,兼容各浏览器)。 - 传参方式:Vue 可直接在模板传参(
@click="handleClick(id, $event)"),React 在函数中绑定(onClick={() => handleClick(id, e)})。
示例:带参数的点击事件
Vue:
<template>
<button @click="handleClick(123, $event)">点击</button>
</template>
<script setup>
const handleClick = (id, e) => {
console.log(id, e.target);
};
</script>
React:
const EventDemo = () => {
const handleClick = (id, e) => {
console.log(id, e.target);
};
return (
<button onClick={(e) => handleClick(123, e)}>点击</button>
);
};
四、进阶特性:路由、状态库、样式
1. 路由:vue-router vs react-router-dom
| 功能 | vue-router@4 | react-router-dom@6 |
|---|---|---|
| 路由配置 | createRouter + routes 数组 | createBrowserRouter + routes 数组 |
| 导航组件 | <router-link to="/home">首页</router-link> | <Link to="/home">首页</Link> |
| 路由出口 | <router-view /> | <Outlet /> |
| 参数获取 | useRoute().params.id | useParams().id |
| 编程式导航 | useRouter().push('/home') | useNavigate()(('/home')) |
示例:React 路由基本用法
// 路由配置(router.js)
import { createBrowserRouter } from 'react-router-dom';
import Home from './Home';
import User from './User';
const router = createBrowserRouter([
{ path: '/', element: <Home /> },
{ path: '/user/:id', element: <User /> }, // 动态路由参数
]);
// 根组件(App.jsx)
import { RouterProvider } from 'react-router-dom';
import router from './router';
const App = () => <RouterProvider router={router} />;
// User 组件获取参数
import { useParams } from 'react-router-dom';
const User = () => {
const { id } = useParams(); // 获取路由参数
return <div>用户 ID:{id}</div>;
};
2. 状态管理:Pinia vs Redux Toolkit
Vue 常用 Pinia 管理全局状态,React 常用 Redux Toolkit(RTK),核心思想类似(集中式状态管理),但 API 不同。
| 功能 | Pinia | Redux Toolkit |
|---|---|---|
| 状态定义 | defineStore 定义 store,直接修改状态 | createSlice 定义 slice,通过 reducer 更新状态 |
| 异步逻辑 | actions 中直接写 async/await | createAsyncThunk 定义异步 action |
| 状态获取 | useStore() 直接获取 | useSelector 选择状态 |
| 状态更新 | store.count++ 或 store.increment() | dispatch(increment()) |
示例:计数器状态管理
Pinia:
// store/counter.js
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
actions: {
increment() { this.count++; }
}
});
// 组件中使用
import { useCounterStore } from './store/counter';
const Counter = () => {
const store = useCounterStore();
return <button @click={store.increment}>{store.count}</button>;
};
Redux Toolkit:
// store/counterSlice.js
import { createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: { count: 0 },
reducers: {
increment: (state) => { state.count++; } // RTK 内部支持直接修改(Immer 库)
}
});
export const { increment } = counterSlice.actions;
// store/index.js
import { configureStore } from '@reduxjs/toolkit';
import counterSlice from './counterSlice';
export const store = configureStore({ reducer: { counter: counterSlice.reducer } });
// 组件中使用
import { useSelector, useDispatch } from 'react-redux';
import { increment } from './store/counterSlice';
const Counter = () => {
const count = useSelector(state => state.counter.count);
const dispatch = useDispatch();
return <button onClick={() => dispatch(increment())}>{count}</button>;
};
3. 样式处理:从 scoped 到 CSS-in-JS
Vue 用 scoped 样式隔离,React 无内置方案,常用以下方式:
- CSS Modules:类似 scoped,通过文件名
xxx.module.css实现隔离。 - Styled Components:用 JS 写样式,支持动态样式。
- Tailwind CSS:原子类样式(Vue/React 通用)。
示例:CSS Modules 用法
/* Button.module.css */
.button {
color: white;
background: blue;
}
.active {
background: red;
}
import styles from './Button.module.css';
const Button = ({ isActive }) => (
<button className={`${styles.button} ${isActive ? styles.active : ''}`}>
按钮
</button>
);
五、避坑指南:Vue 转 React 常见误区
-
状态更新异步性:
React 中setState是异步的,不能直接在更新后读取新值:// 错误:无法立即获取更新后的值 const [count, setCount] = useState(0); const handleClick = () => { setCount(count + 1); console.log(count); // 仍为 0(旧值) }; // 正确:用函数式更新或 useEffect 监听 setCount(prev => prev + 1); // 函数式更新确保依赖最新值 -
双向绑定 vs 受控组件:
Vue 用v-model实现双向绑定,React 需手动实现「受控组件」:// 输入框受控组件(替代 v-model) const InputDemo = () => { const [value, setValue] = useState(''); return ( <input value={value} // 绑定状态 onChange={(e) => setValue(e.target.value)} // 监听变化更新状态 /> ); }; -
JSX 必须有根节点:
Vue 模板允许多个根节点,React JSX 必须有唯一根节点(或用<>空标签包裹):// 错误:多个根节点 return ( <div>1</div> <div>2</div> ); // 正确:用空标签包裹 return ( <> <div>1</div> <div>2</div> </> );
六、学习路径:快速掌握 React
-
基础阶段:
- 掌握 JSX 语法和函数组件。
- 吃透
useState(状态)和useEffect(副作用)两个核心 Hooks。 - 练习条件渲染、列表渲染、事件处理。
-
进阶阶段:
- 学习路由(
react-router-dom)和状态管理(Redux Toolkit 或 Context + useReducer)。 - 掌握其他常用 Hooks:
useRef(DOM 引用)、useMemo(缓存计算)、useCallback(缓存函数)。
- 学习路由(
-
实战阶段:
- 用 React 重构你熟悉的 Vue 小项目(如 TodoList、后台表格)。
- 学习生态工具:TypeScript 集成、Vite 构建、测试工具(Jest + React Testing Library)。
总结
Vue 和 React 本质是「用不同方式解决相同问题」,核心能力(组件化、状态驱动)相通。重点关注 JSX 语法、Hooks 逻辑(尤其是 useEffect)、单向数据流 这三个差异点,通过 1-2 个实战项目练习,就能快速上手 React。
更多推荐
所有评论(0)