引言:当 Vue3 遇上 React,一场「语法平权」运动开始了
作为前端开发者,我们总在框架的「舒适区」里打转:
- React 开发者:
useState
、useEffect
、JSX 是信仰,模板语法?不存在的! - Vue3 开发者:
setup
、ref
、reactive
是真理,Hooks?那是异端!
但今天,我们要打破次元壁!用 React 的思维封装 Vue3 的 API,让 Vue3 也能写出 React 风格的代码!
一、为什么需要「Vue-React 缝合怪」?
1. React 开发者的 Vue3 痛点
- 响应式系统不适应 :
ref
、reactive
、computed
让人头大,不如useState
直观。 - 模板语法陌生 :
v-if
、v-for
、v-model
记不住,还是 JSX 香。 - 生命周期差异 :
onMounted
、onUpdated
和useEffect
逻辑不同,容易混淆。
2. 缝合怪的目标
- 用 React 的 Hooks 思维写 Vue3
- 用 JSX 替代模板语法
- 降低 React 开发者学习 Vue3 的成本
二、核心封装:让 Vue3 变成「React 模拟器」
1. 响应式系统:useState
和 useEffect
替代方案
markdown
javascript
// vue-react-style.js
import { ref, watchEffect, computed, onMounted, onUnmounted } from 'vue'
// 类似 React 的 useState
export function useState(initialValue) {
const state = ref(initialValue)
const setState = (newValue) => {
if (typeof newValue === 'function') {
state.value = newValue(state.value)
} else {
state.value = newValue
}
}
return [state, setState]
}
// 类似 React 的 useEffect
export function useEffect(effect, dependencies) {
if (!dependencies) {
watchEffect(effect)
} else {
let cleanup
watchEffect(() => {
const runEffect = () => {
if (cleanup) cleanup()
cleanup = effect()
}
runEffect()
}, {
flush: 'post', // 类似 React 的 componentDidUpdate
})
onUnmounted(() => {
if (cleanup) cleanup()
})
}
}
效果:
useState
替代ref
,直接解构state.value
useEffect
替代watchEffect
,支持依赖数组
2. 生命周期:useMount
和 useUnmount
scss
javascript
// 类似 React 的 componentDidMount
export function useMount(effect) {
onMounted(effect)
}
// 类似 React 的 componentWillUnmount
export function useUnmount(effect) {
onUnmounted(effect)
}
效果:
- 告别
onMounted
,直接用useMount
- 告别
onUnmounted
,直接用useUnmount
3. 事件处理:useEvent
防止闭包陷阱
markdown
javascript
export function useEvent(handler) {
const handlerRef = useRef(handler)
useEffect(() => {
handlerRef.current = handler
}, [handler])
return (...args) => {
return handlerRef.current && handlerRef.current(...args)
}
}
效果:
- 避免 Vue3 事件处理中的闭包问题
- 类似 React 的
useCallback
优化
三、组件封装:用 JSX 写 Vue3 组件
1. 函数式组件(<script setup>
风格)
xml
javascript
// Counter.vue
<script setup>
import { useState, useEffect, useEvent } from './vue-react-style'
function Counter() {
const [count, setCount] = useState(0)
const handleIncrement = useEvent(() => {
setCount(c => c + 1)
})
const handleDecrement = useEvent(() => {
setCount(c => c - 1)
})
useEffect(() => {
console.log('Count changed:', count.value)
}, [count])
return () => (
<div>
<h2>Count: {count.value}</h2>
<button onClick={handleDecrement}>-</button>
<button onClick={handleIncrement}>+</button>
</div>
)
}
</script>
效果:
- 完全 JSX 写法,告别
template
useState
+useEffect
组合拳
2. 类组件风格(Composition API 模拟)
xml
javascript
// ClassComponent.vue
<script>
import { defineComponent } from 'vue'
import { useState, useEffect } from './vue-react-style'
export default defineComponent({
props: ['initialCount'],
setup(props) {
const [count, setCount] = useState(props.initialCount || 0)
const increment = () => setCount(c => c + 1)
const decrement = () => setCount(c => c - 1)
useEffect(() => {
document.title = `Count: ${count.value}`
}, [count])
return {
count,
increment,
decrement
}
}
})
</script>
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
</template>
效果:
setup
函数返回对象,类似 React 类组件的this.state
- 仍然支持 Vue 模板语法(可选)
四、状态管理:Redux 风格封装
markdown
javascript
// createStore.js
import { reactive, readonly } from 'vue'
export function createStore(reducer, initialState) {
const state = reactive(initialState)
const listeners = new Set()
function getState() {
return readonly(state)
}
function dispatch(action) {
const newState = reducer(state, action)
Object.assign(state, newState)
listeners.forEach(listener => listener())
return action
}
function subscribe(listener) {
listeners.add(listener)
return () => listeners.delete(listener)
}
return {
getState,
dispatch,
subscribe
}
}
// 在组件中使用
import { useSelector, useDispatch } from './vue-react-style'
function TodoList() {
const todos = useSelector(state => state.todos)
const dispatch = useDispatch()
return (
<div>
{todos.map(todo => (
<p key={todo.id}>{todo.text}</p>
))}
<button onClick={() => dispatch({ type: 'ADD_TODO', text: 'New Task' })}>
Add Todo
</button>
</div>
)
}
效果:
createStore
模拟 ReduxuseSelector
+useDispatch
组合拳
五、总结:缝合怪的优缺点
✅ 优点
- 降低学习成本:React 开发者可以快速上手 Vue3
- 统一开发体验:用 Hooks 思维写所有前端代码
- JSX 自由:告别模板语法,享受 React 式的组件化
❌ 缺点
- 性能损耗:封装层可能带来额外开销
- Vue 特性丢失 :如
v-model
、v-slot
等指令难以完美模拟 - 长期维护:建议逐渐适应 Vue 原生 API
结语:框架只是工具,思维才是核心
无论是 React 还是 Vue3,最终目标都是高效开发、可维护代码 。
「Vue-React 缝合怪」 只是一个过渡方案,但它能帮你:
✅ 快速熟悉 Vue3 的响应式系统
✅ 理解 Vue3 的 Composition API 设计思想
✅ 最终选择最适合项目的框架
最后送上一句真理:
"前端框架之争,本质是开发者之间的'信仰战争'。但真正的高手,都能在所有框架里找到乐趣。"
(完)
🔥 关注我,解锁更多前端骚操作!