前言
本文从多个角度对比 React ,但多以实践为主,特别适合写过 Vue2 和 React 18,但是又没写过 Vue3的同学
差异
双向绑定
1 2 3 4 5 6 7 8 9 10 11 12 13
| <script setup> const modelValue = defineModel()
// 读取值 console.log(modelValue.value)
// 更新值 modelValue.value = '新的值' </script>
<template> <input v-model="modelValue" /> </template>
|
主要特点:
defineModel返回一个响应式的ref,可以直接读取和修改值
它自动处理了:
- props的定义(默认prop名为modelValue)
- emit事件的定义(默认事件名为update:modelValue)
- 值的同步更新
React 没有双向绑定的语法糖
父子组件传值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| // react // 子 props.onSuccess()
// 父 onSuccess={() => {}}
// vue // 子 const emit = defineEmits(['on-success']) emit('on-success')
// 父 @on-success="getTableData"
|
和 Vue2 差不多
modelValue 类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <template> <div class="flex gap-2"> <!-- 遍历数组渲染标签 --> <el-tag v-for="tag in modelValue" :key="tag" :closable="editable" ... > {{ tag }} </el-tag> ... </div> </template>
<script setup> const modelValue = defineModel<string[]>() </script>
|
modelValue 是数组类型是由组件的设计目的决定的
React 需要显式定义数据类型
watchEffect
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| // watchEffect:自动追踪依赖 watchEffect(() => { console.log(count.value) // 自动追踪 count })
// watch:需要明确指定要监听的数据 watch(count, (newValue) => { console.log(newValue) })
// 可以手动停止监听 const stop = watchEffect(() => { // ... }) stop() // 停止监听
// 可以控制执行时机 watchEffect(() => { // ... }, { flush: 'post' // 'pre' | 'post' | 'sync' })
// 可以在清理时执行一些操作 watchEffect((onCleanup) => { const timer = setInterval(() => {}, 1000) onCleanup(() => clearInterval(timer)) })
|
watchEffect的特点:
- 会立即执行一次
- 自动追踪内部使用的响应式数据
- 当这些响应式数据变化时,整个回调函数会重新执行
React 通过 useEffect 进行监听
Hooks 对应
|
|
|
React |
Vue3 |
备注 |
useState |
ref 或reactive |
用于定义响应式状态。Vue3的ref 用于基本数据类型,reactive 用于对象。 |
useEffect |
watchEffect 或watch |
用于处理副作用。Vue3的watchEffect 自动跟踪依赖,而watch 可以指定依赖。 |
useMemo |
computed |
用于缓存计算结果。Vue3的computed 是响应式的,与React的useMemo 类似。 |
useCallback |
|
Vue3中通常通过ref 或reactive 结合函数实现类似功能。 |
useContext |
provide 和inject |
用于依赖注入,Vue3的provide 和inject 功能类似。 |
useReducer |
|
Vue3的响应式系统通常可以替代useReducer 。 |
useRef |
ref |
用于创建一个引用,以便在组件中访问DOM元素或其他值。Vue3的ref 与React的useRef 功能类似,都用于存储可变的值。 |
useLayoutEffect |
watch 或watchEffect |
用于在DOM更新后立即同步执行回调函数。Vue3的watch 或watchEffect 可以实现类似功能,通过设置flush 选项为'post' ,可以在DOM更新后立即执行回调。 |
|
shallowRef |
shallowRef 是ref 的浅层形式,用于创建一个响应式引用对象。它只对.value 的访问进行响应式处理,而不会对深层嵌套的对象进行递归响应式转换。 |
自定义Hooks |
自定义Hooks |
两者都支持自定义Hooks,用于逻辑复用。 |
|
|
|
自定义 Hooks
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import { shallowRef } from 'vue' import { tryOnMounted, useEventListener } from '@vueuse/core'
const width = shallowRef(0) const height = shallowRef(0)
export const useWindowResize = (cb) => { const onResize = () => { width.value = window.innerWidth height.value = window.innerHeight if (cb && typeof cb === 'function') { cb(width.value, height.value) } }
tryOnMounted(onResize) useEventListener('resize', onResize, { passive: true }) return { width, height } }
// 引用自 Gin-vue-admin 项目
|
与 React 自定义 Hooks 区别不大
状态中心
对比 jotai 和 pinia
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| // react const userAtom = atom({ name: 'John', age: 30 }); const [count, setCount] = useAtom(userAtom); const createDerivedAtom = (userAtom, factor) => { return atom((get) => get(userAtom)?.name); }
// vue export const useAppStore = defineStore('app', () => { const data= reactive({ name: 'John', age: 30 }) const currentName = computed(() => data.name || '')
return { data, currentName } }) const appStore = useAppStore() appStore.data.name
|
React 倾向于原子式 API,而 pinia 则和 Vuex 差不多。
nextTick
vue 可以通过 $nextTick 控制在页面渲染或者数据更新后的操作
react 一般通过 useEffect 控制,但是微任务执行是有顺序的,所以偶尔会使用 setTimeout(()⇒{},0)
来等待
一些依赖
aHooks
vue 用 vueuse 代替,其实都差不多的
路由
两者几乎是一模一样的。
此处 React 使用 nextjs 提供的路由管理
1 2 3 4 5 6 7 8 9
| import { useRouter } from 'vue-router' const router = useRouter() const route = useRouter()
import { useRouter, usePathname, useSearchParams } from 'next/navigation'; const router = useRouter(); const pathname = usePathname(); const searchParams = useSearchParams();
|
其他
再就是些原理上的不同了,比如模板语法vs Jsx,响应式原理,渲染过程等