前言
本文从多个角度对比 React ,但多以实践为主,特别适合写过 Vue2 和 React 18,但是又没写过 Vue3的同学
差异
双向绑定
<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 没有双向绑定的语法糖
父子组件传值
// react
// 子
props.onSuccess()
// 父
onSuccess={() => {}}
// vue
// 子
const emit = defineEmits(['on-success'])
emit('on-success')
// 父
@on-success="getTableData"
和 Vue2 差不多
modelValue 类型
<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
// 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
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
// 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 提供的路由管理
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,响应式原理,渲染过程等