Vue 开发者的 React 实战指南:快速入门与思维转换

原创
01/09 10:24
阅读数 45

作为一名 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 = () =&gt; {
    setMessage('Clicked!');
  };
  
  return (
    <div classname="hello">
      <h1>{message}</h1>
      <button onclick="{handleClick}">点击</button>
    </div>
  );
}

export default HelloWorld;

主要区别:

  1. React 使用 JSX 而不是模板语法
  2. React 使用 useState 管理状态,而不是 data 选项
  3. React 的事件处理器直接定义为函数
  4. 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 (
    &lt;&gt;
      <input value="{text}" onChange="{(e)" => setText(e.target.value)} 
      /&gt;
      <p>{text}</p>
    
  );
}

主要区别:

  1. React 没有双向绑定语法糖
  2. React 需要手动处理 onChange 事件
  3. 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>
  );
}

主要区别:

  1. React 使用 JavaScript 的条件运算符
  2. React 可以直接在 JSX 中使用表达式
  3. 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 =&gt; (
        <li key="{item.id}">{item.text}</li>
      ))}
    </ul>
  );
}

主要区别:

  1. React 使用 map 方法而不是 v-for 指令
  2. React 的 key 属性直接写在元素上
  3. 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(() =&gt; {
    return todos.filter(todo =&gt; !todo.completed).length;
  }, [todos]);
  
  const addTodo = () =&gt; {
    if (!newTodo.trim()) return;
    
    setTodos([
      ...todos,
      {
        id: Date.now(),
        text: newTodo,
        completed: false
      }
    ]);
    
    setNewTodo('');
  };
  
  const toggleTodo = (id) =&gt; {
    setTodos(todos.map(todo =&gt;
      todo.id === id
        ? { ...todo, completed: !todo.completed }
        : todo
    ));
  };
  
  const removeTodo = (id) =&gt; {
    setTodos(todos.filter(todo =&gt; todo.id !== id));
  };
  
  return (
    <div classname="todo-list">
      <div classname="todo-input">
        <input value="{newTodo}" onChange="{(e)" => setNewTodo(e.target.value)}
          onKeyPress={(e) =&gt; e.key === 'Enter' &amp;&amp; addTodo()}
          placeholder="输入待办事项"
        /&gt;
        <button onclick="{addTodo}">添加</button>
      </div>
      
      <ul classname="todo-items">
        {todos.map(todo =&gt; (
          <li key="{todo.id}" classname="{todo.completed" ? 'completed' : ''}>
            <input type="checkbox" checked="{todo.completed}" onChange="{()" => toggleTodo(todo.id)}
            /&gt;
            <span>{todo.text}</span>
            <button onclick="{()" => removeTodo(todo.id)}&gt;删除</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;
}

主要区别:

  1. 状态管理方式不同
    • Vue 使用 data 和 methods
    • React 使用 useState 和普通函数
  2. 计算属性实现不同
    • Vue 使用 computed
    • React 使用 useMemo
  3. 事件处理方式不同
    • Vue 使用 @ 或 v-on 指令
    • React 使用驼峰命名的事件属性
  4. 样式处理方式不同
    • Vue 使用 scoped style
    • React 使用单独的 CSS 文件

开发思维转换

  1. 声明式编程

    • Vue 和 React 都是声明式的
    • 但 React 更倾向使用 JavaScript 的方式解决问题
    • Vue 提供了更多的模板语法糖
  2. 数据不可变性

    • Vue 的响应式系统允许直接修改数据
    • React 推崇数据不可变性,总是创建新的状态
  3. 组件设计

    • Vue 的单文件组件更注重关注点分离
    • React 推崇 JSX,将模板和逻辑紧密结合
  4. 状态管理

    • Vue 的响应式系统使状态管理更简单
    • React 需要手动管理状态更新

注意事项

  1. 避免直接修改状态

    // 错误
    const [todos, setTodos] = useState([]);
    todos.push(newTodo);  // 直接修改状态
    
    // 正确
    setTodos([...todos, newTodo]);  // 创建新的状态
    
  2. 正确使用 useEffect

    // 错误
    useEffect(() =&gt; {
      // 没有依赖数组,每次渲染都会执行
    });
    
    // 正确
    useEffect(() =&gt; {
      // 只在依赖项变化时执行
    }, [dependency]);
    
  3. 合理使用 useMemo 和 useCallback

    // 不必要的使用
    const simple = useMemo(() =&gt; a + b, [a, b]);  // 简单计算不需要缓存
    
    // 合理使用
    const complex = useMemo(() =&gt; expensiveCalculation(a, b), [a, b]);
    
  4. 避免过度解构

    // 可能导致重复渲染
    const { value1, value2, value3 } = useContext(MyContext);
    
    // 更好的方式
    const context = useContext(MyContext);
    

调试技巧

  1. 使用 React Developer Tools

    • 安装 Chrome 扩展
    • 查看组件结构
    • 检查 props 和 state
    • 性能分析
  2. 使用 console.log 调试

    useEffect(() =&gt; {
      console.log('Component mounted');
      return () =&gt; console.log('Component will unmount');
    }, []);
    
  3. 使用 React.StrictMode

    import { StrictMode } from 'react';
    
    root.render(
      <strictmode>
        <app />
      </strictmode>
    );
    

小结

  1. React 和 Vue 的主要区别:

    • 模板语法 vs JSX
    • 响应式系统 vs 单向数据流
    • 指令系统 vs JavaScript 表达式
    • 生命周期 vs Hooks
  2. React 开发的关键点:

    • 理解 JSX
    • 掌握 Hooks
    • 遵循不可变性
    • 合理使用缓存
  3. 开发建议:

    • 多写 JavaScript,少依赖框架特性
    • 保持组件纯函数的特性
    • 合理拆分组件
    • 注意性能优化

下一篇文章,我们将深入探讨 React 的状态管理,帮助你更好地理解和使用 React 的状态管理方案。

如果觉得这篇文章对你有帮助,别忘了点个赞 👍

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
0 收藏
0
分享
返回顶部
顶部