Vue3 转 React:18 项对照表 + 5 个必踩的坑(附 7 天上手路线)

472 阅读6分钟

最近在换工作,把 BOSS 直聘和其它招聘软件刷了一遍,发现一件事:招前端的岗位,十有八九写的是 React。

而我写了三年 Vue3,拿得出手的项目经验也基本都是 Vue 的——经验够,技术栈不对,就这么卡住了。

所以不是"React 更流行所以我学",是很现实的原因——岗位在那儿,我得过去。好消息是,你不是从零学,你只是换一套语法,把脑子里的心智模型平移过去。

有人会说现在 AI 都能写代码了,还学这些干嘛。我的答案是:AI 写的代码,总得有人看得懂。

这篇文章不教你 React 基础。它假设你懂 Vue3 的 ref、computed、watch,只做一件事:把你已经会的东西,逐项映射到 React。

通篇是一张对照表。


先说结论:映射关系

Vue3 概念React 对应相似度
ref / reactiveuseState高
computeduseMemo高
watch / watchEffectuseEffect中(差异最大)
模板 ref 引用 DOMuseRef中
vue-router useRoutereact-router useParams高
class="x"className="x"完全等价

记住一条主线:Vue 帮你自动追踪依赖,React 要你手动声明依赖。这是后面所有差异的根源。


1. 状态:useState ≈ ref

Vue 里你这么写:

const count = ref(0)
count.value = 1

React 里是:

const [count, setCount] = useState(0)
setCount(count + 1)

对照:

维度Vue3React
定义const count = ref(0)const [count, setCount] = useState(0)
读值count.value直接用 count
改值count.value = 1setCount(count + 1)
触发更新改 .value 自动调 setXxx 才更新

关键差别:React 没有 .value。count 就是值本身,setCount 是唯一能改它的函数。你改了普通变量不会刷新界面——这是新手最常见的坑。


2. 副作用:useEffect vs watch / watchEffect(重点,差异最大)

2.1 React 基础用法

import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

2.2 两类 effect:留没留下"活物"

判断一个 effect 要不要清理,问自己一句:"它执行完,往外面留下了活物吗?"

活物指:监听器、定时器、订阅、实例、请求。

  • 无需清除的 effect:执行完就结束,不留下东西。比如上面改 document.title,跑完就没了。
  • 需要清除的 effect:留下了订阅/监听,组件卸载时要手动断开,否则内存泄漏。
useEffect(() => {
  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }
  ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
  return () => {
    ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
  };
});

注意那个 return 的函数——它就是清理函数,组件卸载时 React 会调用它。

2.3 什么写法会导致无限循环

这是 React 新手掉得最狠的坑。先看懂本质,再记修复。

无限循环的本质 = 三个环节连成闭环:

  1. effect 执行:渲染之后自动跑
  2. setState 状态更新:没有这一步,链就断了,安全
  3. 重新渲染:组件函数再跑一遍,渲染后检查依赖:变了 → 回到①;没变 → 停

无限循环 = 闭环完整。修复思路只有一种:断掉其中一环。

最常见的三种成因:

成因说明断环方法
无依赖数组 + setStateeffect 每次渲染都跑,一跑就 setState,又渲染,又跑…加依赖数组 []
依赖引用类型,每次渲染新建{a:1} 每次都是新引用,Object.is 永远不等依赖原始值,或 useMemo 稳定引用
setState 产生的新引用又进依赖自己造的依赖自己改用函数式更新 setX(x => x+1)

2.4 Vue 这边:watch vs watchEffect

// watch:只追踪你明确写的数据源,依赖精确可控
watch(id, (newId, oldId) => {
  // 仅 id 变才触发
})

// watchEffect:同步执行时自动追踪访问到的所有响应式属性
watchEffect(() => {
  // 访问了谁就追踪谁,更方便但关系不那么明显
})
维度watchwatchEffect
依赖来源手动指定数据源自动追踪访问到的响应式属性
触发时机数据源确实改变才触发同步执行期间自动收集
精确度高,能精确控制方便但依赖关系有时不明

2.5 Vue 清理副作用(3.5+)

import { watch, onWatcherCleanup } from 'vue'

watch(id, (newId) => {
  const controller = new AbortController()
  fetch(`/api/${newId}`, { signal: controller.signal }).then(() => { /* ... */ })
  onWatcherCleanup(() => {
    controller.abort() // 终止过期请求
  })
})

老写法用回调的第三个参数:

watch(id, (newId, oldId, onCleanup) => {
  onCleanup(() => { /* 清理逻辑 */ })
})
watchEffect((onCleanup) => {
  onCleanup(() => { /* 清理逻辑 */ })
})

对照小结:Vue 的 watch/watchEffect 更"聪明"(自动追踪),React 的 useEffect 更"笨"但更可控(依赖要你手写)。这是两种哲学,不是谁更好。


3. 派生数据:useMemo ≈ computed

Vue 的 computed:

const double = computed(() => count.value * 2)

React 的 useMemo:

const double = useMemo(() => count * 2, [count])

从三个维度对照:

维度Vue computedReact useMemo
依赖怎么来自动追踪手动声明数组 [count]
依赖变了重算重算
用来稳定引用基本不需要经常需要(React 靠 Object.is 比引用)

第三行是重点。React 里 useMemo 不止为了"算得快",更常为了"引用稳定"。因为 React 默认用 Object.is 比较前后值,你每次渲染 new 一个对象/数组/函数,引用就变了,会触发不该触发的重渲染或 effect。这时候 useMemo(或 useCallback)就是用来把引用钉住的。

Vue 不需要这一步,因为响应式系统自己管引用。


4. useCallback 就是 useMemo 的语法糖

记住一句话:

useCallback(fn, deps)
// 等价于
useMemo(() => fn, deps)

它专门用来稳定函数的引用。场景:你要把一个函数作为 prop 传给子组件,又不想每次渲染都造个新函数导致子组件无谓重渲染——用 useCallback 包一下。


5. useRef:跨渲染一直存在的盒子

const timer = useRef(null)
timer.current = setInterval(...)

useRef 是一个跨越渲染一直存在的盒子。你改 timer.current,界面不会重新渲染。

这里要纠一个常见误解:useRef 不是 Vue 的 reactive。

  • reactive 改了值 → 界面自动更新(响应式)
  • useRef 改了值 → 界面不动,它只是个能活过多次渲染的容器

useRef 真正用在两处:① 存定时器/订阅这种"不想触发渲染的变量";② 模板里拿 DOM 节点(等价于 Vue 的模板 ref)。


6. CSS:class → className(附一个真实踩坑)

Vue:

<div class="card"></div>

React:

<div className="card"></div>

就这一个词不一样。但有个真实坑我替你踩过:

在 Vite + React + TS 项目里,如果你写 import './taskListPage.css',编辑器可能报:

src/pages/TaskListPage.tsx:4:39 - error TS2882:
Cannot find module or type declarations for side-effect import of './taskListPage.css'.

原因:TS 不认识 CSS 这种"副作用导入"。修法是新建一个 src/vite-env.d.ts,放一行:

/// <reference types="vite/client" />

Vite 的类型声明就位后,TS 就知道 .css 导入是合法的了。这个文件 Vite 官方脚手架其实自带,自己建项目时容易漏。


7. 路由:useParams vs useRoute

Vue:

import { useRoute } from 'vue-router'
const route = useRoute()
route.params.id
route.query.tab

React(react-router v6/v7):

import { useParams, Link } from 'react-router-dom'

// 跳转:Link 只改 URL
<Link to={`/tasks/${taskNo}`}>详情</Link>

// 读取:在目标页用 useParams 取
const { taskNo } = useParams()

一个很多人绕不过去的弯:<Link to> 负责"改 URL",useParams() 负责"读参数",两者是解耦的。不是"用了 Link 跳转才配用 useParams"——任何匹配到 :taskNo 这个路由的页面,都能用 useParams 拿到它。跳转和取参是两条独立的线。


18 项速查表(收尾)

把上面拆细,就是你要的 18 项对照:

#概念Vue3React关键差异
1定义状态ref()useState()React 返回 [值, 改值函数]
2读状态x.value直接用 xReact 无 .value
3改状态x.value =setX()React 只能靠 setter
4基础副作用watchEffectuseEffectReact 依赖手写
5不清除的 effect执行完即止useEffect 无 return同理
6需清除的 effectonCleanupreturn () => {...}清理函数写法不同
7无限循环成因极少三环闭环React 高频坑
8无限循环修复—断环三法空依赖/原始值/函数式更新
9精确依赖watchuseEffect([deps])都手动
10自动依赖watchEffect(无原生对应)React 需手动
11Vue 清理 3.5+onWatcherCleanup—Vue 专属
12派生值computeduseMemo都缓存
13依赖来源自动追踪手动声明核心差异
14稳定引用需求基本不需经常需要React Object.is
15稳定函数—useCallback语法糖
16跨渲染容器—useRef≠ reactive
17DOM 引用模板 refuseRef同用途
18样式类名classclassName仅一词之差

7 天上手路线(可以直接照抄)

这是我自己的排期,每天大概 1-2 小时。顺序和节奏你都可以按自己的情况调,不用照搬。

天内容对应上表
Day 1心智切换:JSX + useState1–3
Day 2useEffect 基础 + 清理4–6
Day 3无限循环专题7–8
Day 4computed ↔ useMemo12–14
Day 5useCallback + useRef15–17
Day 6路由 Link / useParams18 之外
Day 7综合:抄一个自己的小页全部

Vue 和 React 不是两个语言,是两种"谁来管理依赖"的哲学。Vue 替你记,React 让你写。你 Vue3 的老底子在,平移过去最多一周。真正要小心的只有一处:React 的依赖要你亲手声明,漏了就无限循环,多了就白渲染。

把这张表存好。下次卡住,回来查第几行。


写在最后:这份对照表是我自己转 React 时边学边记的。接下来转型路上真正踩过的坑——Node、TS、部署这些,我还会继续写,学到哪儿写到哪儿,不按固定顺序。如果你也是从 Vue 过来的,欢迎在评论区说说你卡在哪儿,我可能正好也踩过。

另外也想问一句:AI 都能写 React 了,你觉得还有必要系统学一遍吗? 想听听大家的答案。