Vue与React的核心差异
Vue3采用组合式API(Composition API),提供了setup()函数和明确的响应式系统,以及清晰的生命周期函数如onMounted、onBeforeMount等。而React则采用函数式组件和Hooks模式,状态管理通过useState,副作用通过useEffect处理。
创建useVue自定义Hook
下面是一个模拟Vue3开发体验的useVue。使用 useVue,使其更接近 Vue3 的开发体验。添加计算属性、监听器、方法、生命周期钩子等功能,并优化状态管理方式。
import { useState, useEffect, useRef, useCallback, useContext, createContext } from 'react';
// 创建上下文用于依赖注入
const VueContext = createContext();
export function useVue(options) {
// 状态管理
const [state, setState] = useState({});
const stateRef = useRef({});
const mountedRef = useRef(false);
const watchersRef = useRef({});
const computedRef = useRef({});
// 处理data选项
if (options.data) {
const data = typeof options.data === 'function' ? options.data() : options.data;
stateRef.current = { ...data };
setState({ ...data });
}
// 处理computed计算属性
if (options.computed) {
Object.keys(options.computed).forEach(key => {
const computedFn = options.computed[key];
if (typeof computedFn === 'function') {
computedRef.current[key] = computedFn;
} else if (computedFn.get) {
computedRef.current[key] = computedFn.get;
}
});
}
// 处理methods方法
const methodsRef = useRef({});
if (options.methods) {
Object.keys(options.methods).forEach(key => {
methodsRef.current[key] = options.methods[key].bind(stateRef.current);
});
}
// 处理watch监听器
if (options.watch) {
Object.keys(options.watch).forEach(key => {
watchersRef.current[key] = options.watch[key];
});
}
// 处理provide
if (options.provide) {
const provideValue = typeof options.provide === 'function'
? options.provide(stateRef.current)
: options.provide;
// 使用React的Context提供值
const Provider = ({ children }) => (
<VueContext.Provider value={provideValue}>
{children}
</VueContext.Provider>
);
stateRef.current.Provider = Provider;
}
// 处理inject
if (options.inject) {
const injectedValues = useContext(VueContext);
if (Array.isArray(options.inject)) {
options.inject.forEach(key => {
stateRef.current[key] = injectedValues[key];
});
} else {
Object.keys(options.inject).forEach(key => {
const injectKey = options.inject[key];
stateRef.current[key] = injectedValues[injectKey];
});
}
}
// 创建ref引用
const refsRef = useRef({});
const createRef = useCallback((name) => {
return (el) => {
refsRef.current[name] = el;
};
}, []);
// 生命周期钩子处理
useEffect(() => {
// onMounted
if (options.onMounted && !mountedRef.current) {
options.onMounted.call(stateRef.current);
mountedRef.current = true;
}
// onUpdated
if (options.onUpdated && mountedRef.current) {
options.onUpdated.call(stateRef.current);
}
// 监听器处理
Object.keys(watchersRef.current).forEach(key => {
const oldValue = stateRef.current[key];
const newValue = state[key];
if (oldValue !== newValue) {
watchersRef.current[key].call(stateRef.current, newValue, oldValue);
}
});
// 更新引用
stateRef.current = { ...stateRef.current, ...state };
}, [state]);
// onBeforeUnmount
useEffect(() => {
return () => {
if (options.onBeforeUnmount) {
options.onBeforeUnmount.call(stateRef.current);
}
};
}, []);
// 创建响应式状态更新函数
const setStateValue = useCallback((key, value) => {
setState(prev => ({ ...prev, [key]: value }));
}, []);
// 创建批量状态更新函数
const setStateBatch = useCallback((newState) => {
setState(prev => ({ ...prev, ...newState }));
}, []);
// 暴露API
const vueApi = {
// 状态访问
$state: state,
$data: stateRef.current,
// 状态更新
$setState: setStateValue,
$setBatch: setStateBatch,
// 计算属性
$computed: computedRef.current,
// 方法
$methods: methodsRef.current,
// 引用
$refs: refsRef.current,
$ref: createRef,
// 生命周期
$onMounted: options.onMounted,
$onUpdated: options.onUpdated,
$onBeforeUnmount: options.onBeforeUnmount,
// 上下文
$provide: options.provide,
$inject: options.inject,
Provider: stateRef.current.Provider,
};
// 返回合并后的API和状态
return {
...state,
...methodsRef.current,
...computedRef.current,
...vueApi,
};
}
// 生命周期钩子辅助函数
export const onMounted = (fn) => ({ onMounted: fn });
export const onUpdated = (fn) => ({ onUpdated: fn });
export const onBeforeUnmount = (fn) => ({ onBeforeUnmount: fn });
// 计算属性辅助函数
export const computed = (getter) => ({ computed: { _: getter } });
// 监听器辅助函数
export const watch = (source, callback) => ({ watch: { [source]: callback } });
// 方法辅助函数
export const methods = (methodsObj) => ({ methods: methodsObj });
// 数据辅助函数
export const data = (dataObj) => ({ data: dataObj });
// 依赖注入辅助函数
export const provide = (provideObj) => ({ provide: provideObj });
export const inject = (injectKeys) => ({ inject: injectKeys });
使用示例
可以用类似Vue3的方式编写React组件:
import React from 'react';
import { useVue, onMounted, onUpdated, onBeforeUnmount, computed, watch, methods, data, provide, inject } from './useVue';
function ParentComponent() {
const vue = useVue(
data(() => ({
count: 0,
message: 'Hello Vue in React!',
})),
computed({
doubleCount() {
return this.count * 2;
},
}),
methods({
increment() {
this.count++;
},
decrement() {
this.count--;
},
reset() {
this.count = 0;
},
}),
watch('count', (newVal, oldVal) => {
console.log(`Count changed from ${oldVal} to ${newVal}`);
}),
provide({
sharedMessage: 'This is a shared message',
}),
onMounted(() => {
console.log('Component mounted!');
}),
onUpdated(() => {
console.log('Component updated!');
}),
onBeforeUnmount(() => {
console.log('Component will unmount!');
})
);
return (
<div>
<h1>{vue.message}</h1>
<p>Count: {vue.count}</p>
<p>Double Count: {vue.doubleCount}</p>
<button onClick={vue.increment}>Increment</button>
<button onClick={vue.decrement}>Decrement</button>
<button onClick={vue.reset}>Reset</button>
<vue.Provider>
<ChildComponent />
</vue.Provider>
</div>
);
}
function ChildComponent() {
const vue = useVue(
inject(['sharedMessage']),
data(() => ({
childMessage: '',
})),
onMounted(() => {
this.childMessage = this.sharedMessage + ' (from child)';
})
);
return (
<div>
<h2>Child Component</h2>
<p>{vue.childMessage}</p>
</div>
);
}
优化点说明
- 更接近Vue3的API设计:
- 使用
data()、computed()、methods()等辅助函数定义组件 - 生命周期钩子使用
onMounted、onUpdated、onBeforeUnmount - 支持
watch监听器 - 支持
provide/inject依赖注入
- 使用
- 状态管理优化:
- 自动合并状态和方法,可以直接通过
this访问 - 提供
$setState和$setBatch方法进行状态更新 - 状态更新自动触发重新渲染
- 自动合并状态和方法,可以直接通过
- 响应式系统模拟:
- 计算属性自动计算并缓存结果
- 监听器自动检测状态变化并触发回调
- 模板引用支持:
- 提供
$ref方法创建引用,类似Vue的ref属性
- 提供
- 依赖注入:
- 使用React的Context实现Vue风格的provide/inject
- 性能优化:
- 使用
useRef保存持久化数据,避免不必要的重新渲染 - 合理使用
useCallback优化函数引用
- 使用
缺点
- 性能开销:封装层会增加一定的性能开销
- 不完全等价:React和Vue的响应式系统底层实现不同(Vue3使用Proxy,React使用状态更新触发重渲染)
- 社区支持:这种自定义方式无法获得React社区的广泛支持和最佳实践
使用建议
- 逐步过渡:先使用
useVue作为学习工具,然后逐步尝试React原生Hooks - 理解底层原理:了解React和Vue在响应式系统、渲染机制上的差异
- 团队协作:在团队项目中,确保其他成员理解这种封装方式
- 性能考虑:对于复杂应用,可能需要评估这种封装的性能影响