React 转 Vue3

前言

本文从多个角度对比 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>

主要特点:

  1. defineModel返回一个响应式的ref,可以直接读取和修改值

  2. 它自动处理了:

    • 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 refreactive 用于定义响应式状态。Vue3的ref用于基本数据类型,reactive用于对象。
useEffect watchEffectwatch 用于处理副作用。Vue3的watchEffect自动跟踪依赖,而watch可以指定依赖。
useMemo computed 用于缓存计算结果。Vue3的computed是响应式的,与React的useMemo类似。
useCallback Vue3中通常通过refreactive结合函数实现类似功能。
useContext provideinject 用于依赖注入,Vue3的provideinject功能类似。
useReducer Vue3的响应式系统通常可以替代useReducer
useRef ref 用于创建一个引用,以便在组件中访问DOM元素或其他值。Vue3的ref与React的useRef功能类似,都用于存储可变的值。
useLayoutEffect watchwatchEffect 用于在DOM更新后立即同步执行回调函数。Vue3的watchwatchEffect可以实现类似功能,通过设置flush选项为'post',可以在DOM更新后立即执行回调。
shallowRef shallowRefref的浅层形式,用于创建一个响应式引用对象。它只对.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,响应式原理,渲染过程等