作为一名 Vue 开发者,第一次接触 React 时可能会感到困惑。虽然两者都是优秀的前端框架,但在开发思维和实现方式上存在较大差异。本文将通过实战示例,帮助你快速掌握 React 开发的核心概念和基本用法。
开发环境搭建
首先,我们需要搭建 React 开发环境。React 官方提供了脚手架工具 Create React App:
# 创建项目
npx create-react-app my-react-app
# 进入项目目录
cd my-react-app
# 启动开发服务器
npm start
如果你习惯使用 Vite,也可以使用 Vite 创建 React 项目:
# 使用 Vite 创建项目
npm create vite@latest my-react-app -- --template react
# 进入项目目录
cd my-react-app
# 安装依赖
npm install
# 启动开发服务器
npm run dev
基本语法对比
1. 组件定义
Vue 组件(SFC):
<!-- HelloWorld.vue -->
<template>
<div class="hello">
<h1>{{ message }}</h1>
<button @click="handleClick">点击</button>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data() {
return {
message: 'Hello Vue!'
}
},
methods: {
handleClick() {
this.message = 'Clicked!'
}
}
}
</script>
<style scoped>
.hello {
color: blue;
}
</style>
React 组件:
// HelloWorld.jsx
import React, { useState } from 'react';
import './HelloWorld.css';
function HelloWorld() {
const [message, setMessage] = useState('Hello React!');
const handleClick = () => {
setMessage('Clicked!');
};
return (
<div className="hello">
<h1>{message}</h1>
<button onClick={handleClick}>点击</button>
</div>
);
}
export default HelloWorld;
主要区别:
- React 使用 JSX 而不是模板语法
- React 使用 useState 管理状态,而不是 data 选项
- React 的事件处理器直接定义为函数
- React 的样式需要单独引入
2. 数据绑定
Vue 的双向绑定:
<template>
<input v-model="text" />
<p>{{ text }}</p>
</template>
<script>
export default {
data() {
return {
text: ''
}
}
}
</script>
React 的受控组件:
function InputComponent() {
const [text, setText] = useState('');
return (
<>
<input
value={text}
onChange={(e) => setText(e.target.value)}
/>
<p>{text}</p>
</>
);
}
主要区别:
- React 没有双向绑定语法糖
- React 需要手动处理 onChange 事件
- React 使用受控组件模式
3. 条件渲染
Vue 的条件渲染:
<template>
<div>
<h1 v-if="show">显示</h1>
<h1 v-else>隐藏</h1>
</div>
</template>
<script>
export default {
data() {
return {
show: true
}
}
}
</script>
React 的条件渲染:
function ConditionalComponent() {
const [show, setShow] = useState(true);
return (
<div>
{show ? <h1>显示</h1> : <h1>隐藏</h1>}
</div>
);
}
主要区别:
- React 使用 JavaScript 的条件运算符
- React 可以直接在 JSX 中使用表达式
- React 没有特殊的指令语法
4. 列表渲染
Vue 的列表渲染:
<template>
<ul>
<li v-for="(item, index) in items" :key="item.id">
{{ item.text }}
</li>
</ul>
</template>
<script>
export default {
data() {
return {
items: [
{ id: 1, text: '项目 1' },
{ id: 2, text: '项目 2' },
{ id: 3, text: '项目 3' }
]
}
}
}
</script>
React 的列表渲染:
function ListComponent() {
const [items, setItems] = useState([
{ id: 1, text: '项目 1' },
{ id: 2, text: '项目 2' },
{ id: 3, text: '项目 3' }
]);
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.text}</li>
))}
</ul>
);
}
主要区别:
- React 使用 map 方法而不是 v-for 指令
- React 的 key 属性直接写在元素上
- React 可以直接在 JSX 中使用数组方法
实战示例:Todo List
让我们通过一个简单的 Todo List 来实践上述概念。
Vue 版本:
<!-- TodoList.vue -->
<template>
<div class="todo-list">
<div class="todo-input">
<input
v-model="newTodo"
@keyup.enter="addTodo"
placeholder="输入待办事项"
/>
<button @click="addTodo">添加</button>
</div>
<ul class="todo-items">
<li
v-for="todo in todos"
:key="todo.id"
:class="{ completed: todo.completed }"
>
<input
type="checkbox"
v-model="todo.completed"
/>
<span>{{ todo.text }}</span>
<button @click="removeTodo(todo.id)">删除</button>
</li>
</ul>
<div class="todo-stats">
剩余: {{ remaining }} 项
</div>
</div>
</template>
<script>
export default {
data() {
return {
newTodo: '',
todos: []
}
},
computed: {
remaining() {
return this.todos.filter(todo => !todo.completed).length
}
},
methods: {
addTodo() {
if (!this.newTodo.trim()) return
this.todos.push({
id: Date.now(),
text: this.newTodo,
completed: false
})
this.newTodo = ''
},
removeTodo(id) {
this.todos = this.todos.filter(todo => todo.id !== id)
}
}
}
</script>
<style scoped>
.todo-list {
max-width: 400px;
margin: 20px auto;
padding: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.todo-input {
display: flex;
margin-bottom: 20px;
}
.todo-input input {
flex: 1;
padding: 8px;
margin-right: 10px;
}
.todo-items li {
display: flex;
align-items: center;
padding: 8px 0;
border-bottom: 1px solid #eee;
}
.todo-items li.completed span {
text-decoration: line-through;
color: #999;
}
.todo-items li span {
flex: 1;
margin: 0 10px;
}
.todo-stats {
margin-top: 20px;
color: #666;
}
</style>
React 版本:
// TodoList.jsx
import React, { useState, useMemo } from 'react';
import './TodoList.css';
function TodoList() {
const [newTodo, setNewTodo] = useState('');
const [todos, setTodos] = useState([]);
// 使用 useMemo 缓存计算结果
const remaining = useMemo(() => {
return todos.filter(todo => !todo.completed).length;
}, [todos]);
const addTodo = () => {
if (!newTodo.trim()) return;
setTodos([
...todos,
{
id: Date.now(),
text: newTodo,
completed: false
}
]);
setNewTodo('');
};
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id
? { ...todo, completed: !todo.completed }
: todo
));
};
const removeTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
return (
<div className="todo-list">
<div className="todo-input">
<input
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && addTodo()}
placeholder="输入待办事项"
/>
<button onClick={addTodo}>添加</button>
</div>
<ul className="todo-items">
{todos.map(todo => (
<li
key={todo.id}
className={todo.completed ? 'completed' : ''}
>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<span>{todo.text}</span>
<button onClick={() => removeTodo(todo.id)}>删除</button>
</li>
))}
</ul>
<div className="todo-stats">
剩余: {remaining} 项
</div>
</div>
);
}
export default TodoList;
/* TodoList.css */
.todo-list {
max-width: 400px;
margin: 20px auto;
padding: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.todo-input {
display: flex;
margin-bottom: 20px;
}
.todo-input input {
flex: 1;
padding: 8px;
margin-right: 10px;
}
.todo-items li {
display: flex;
align-items: center;
padding: 8px 0;
border-bottom: 1px solid #eee;
}
.todo-items li.completed span {
text-decoration: line-through;
color: #999;
}
.todo-items li span {
flex: 1;
margin: 0 10px;
}
.todo-stats {
margin-top: 20px;
color: #666;
}
主要区别:
- 状态管理方式不同
- Vue 使用 data 和 methods
- React 使用 useState 和普通函数
- 计算属性实现不同
- Vue 使用 computed
- React 使用 useMemo
- 事件处理方式不同
- Vue 使用 @ 或 v-on 指令
- React 使用驼峰命名的事件属性
- 样式处理方式不同
- Vue 使用 scoped style
- React 使用单独的 CSS 文件
开发思维转换
声明式编程
- Vue 和 React 都是声明式的
- 但 React 更倾向使用 JavaScript 的方式解决问题
- Vue 提供了更多的模板语法糖
数据不可变性
- Vue 的响应式系统允许直接修改数据
- React 推崇数据不可变性,总是创建新的状态
组件设计
- Vue 的单文件组件更注重关注点分离
- React 推崇 JSX,将模板和逻辑紧密结合
状态管理
- Vue 的响应式系统使状态管理更简单
- React 需要手动管理状态更新
注意事项
避免直接修改状态
// 错误 const [todos, setTodos] = useState([]); todos.push(newTodo); // 直接修改状态 // 正确 setTodos([...todos, newTodo]); // 创建新的状态
正确使用 useEffect
// 错误 useEffect(() => { // 没有依赖数组,每次渲染都会执行 }); // 正确 useEffect(() => { // 只在依赖项变化时执行 }, [dependency]);
合理使用 useMemo 和 useCallback
// 不必要的使用 const simple = useMemo(() => a + b, [a, b]); // 简单计算不需要缓存 // 合理使用 const complex = useMemo(() => expensiveCalculation(a, b), [a, b]);
避免过度解构
// 可能导致重复渲染 const { value1, value2, value3 } = useContext(MyContext); // 更好的方式 const context = useContext(MyContext);
调试技巧
使用 React Developer Tools
- 安装 Chrome 扩展
- 查看组件结构
- 检查 props 和 state
- 性能分析
使用 console.log 调试
useEffect(() => { console.log('Component mounted'); return () => console.log('Component will unmount'); }, []);
使用 React.StrictMode
import { StrictMode } from 'react'; root.render( <StrictMode> <App /> </StrictMode> );
小结
React 和 Vue 的主要区别:
- 模板语法 vs JSX
- 响应式系统 vs 单向数据流
- 指令系统 vs JavaScript 表达式
- 生命周期 vs Hooks
React 开发的关键点:
- 理解 JSX
- 掌握 Hooks
- 遵循不可变性
- 合理使用缓存
开发建议:
- 多写 JavaScript,少依赖框架特性
- 保持组件纯函数的特性
- 合理拆分组件
- 注意性能优化
下一篇文章,我们将深入探讨 React 的状态管理,帮助你更好地理解和使用 React 的状态管理方案。
如果觉得这篇文章对你有帮助,别忘了点个赞 👍