众所周知,在开发中效率是很重要的能力体现,很多老板都是只看结果不问过程的,作为一个出色的搬砖工人,我们也常常秉持能复制就粘贴,能借鉴就不创作的开发理念。今天,让我们来看下如何在 react
项目中快速迁移 vue3
模块。
react 和 vue3 本身就有很多相似的地方,总体上他们的逻辑的差不多的,无非是几个 api 的切换。
useEffect & watch & watchEffect
这个应该就不用多说了,直接一键替换。需要注意下 watchEffect 需要自己拾取出需要监听的依赖项。
注:下面的 useUpdate 和 useCreation 都是 aHook 的钩子。可以移步这里了解其用法
ref
vue3 的 ref 用来构造基本类型
的响应式更新。react 中没有现成的 api,但我们可以借助 Proxy 实现。
ts
/** 模拟 Vue3 ref api,全局不变值,也能响应式更新基础类型 */
type Ref<T> = {
value: T
}
export function useRefState<T>(initial: T): Ref<T> {
const update = useUpdate()
const Obj = useRef(new Proxy({
value: initial,
}, {
get(target, key) {
if (key === 'value') {
return Reflect.get(target, key)
}
throw new Error('请使用 value 访问值')
},
set(target, key, value) {
if (key === 'value') {
Reflect.set(target, key, value)
update()
return true
} else {
return false
}
},
deleteProperty() {
throw new Error('不允许删除操作')
},
})).current;
return Obj;
}
reactive
vue3 的 reactive 用来构造引用类型
的响应式更新。react 中也没有现成的 api,我们照样可以借助 Proxy 实现。
ts
function observer<T extends Record<string, any>>(initialVal: T, cb: VoidFunction): T {
const proxy = new Proxy<T>(initialVal, {
get(target, key, receiver) {
const res = Reflect.get(target, key, receiver);
const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
if (!descriptor?.configurable && !descriptor?.writable) {
return res;
}
return isPlainObject(res) || Array.isArray(res) ? observer(res, cb) : res;
},
set(target, key, val) {
const ret = Reflect.set(target, key, val);
cb();
return ret;
},
deleteProperty(target, key) {
const ret = Reflect.deleteProperty(target, key);
cb();
return ret;
},
});
return proxy;
}
type RefState<T> = {
value: T,
/** 监察深层变动 */
watcher: unknown,
/** 受控依赖刷新 */
refresh: unknown
}
/** 模拟 Vue3 reactive api,能深层响应式更新对象,通过 watcher 对比 */
export function useReactive<S extends Record<string, any>>(initialState: S): RefState<S> {
const watcher = useRef<unknown>(Object.create(null))
const refresh = useRef<unknown>(Object.create(null))
const update = useUpdate()
const state = useCreation(() => {
return new Proxy(
{
value: observer(initialState, () => {
watcher.current = Object.create(null)
update()
})
},
{
get(target, key) {
switch(key) {
case 'value': {
return Reflect.get(target, key)
}
case 'watcher': {
return watcher.current
}
case 'refresh': {
return refresh.current
}
default: {
throw new Error('通过 value 获取数据,通过 watcher 监察变动,通过 refresh 受控依赖刷新,其他属性值无效')
}
}
},
set(target, key, value) {
if (key === 'refresh') {
refresh.current = Object.create(null)
update()
} else {
target[key] = value
}
return true
}
}
)
}, []);
return state as RefState<S>;
}
注:这里考虑到引用类型可能是深层嵌套,所以我们通过维护一个 watcher 属性进行依赖监控。有时候我们不希望每次变动都触发依赖刷新,所以通过 refresh 属性进行手动刷新
实践
vue
const width = ref(0)
const rect = reactive({width: 0, height: 0})
watch([width, rect], () => {
/** todo something */
})
watchEffect(() => {
console.log(rect.value)
})
react
const width = useRefState(0)
const rect = useReactive({width: 0, height: 0})
useEffect(() => {
/** todo something */
}, [width.value, rect.watcher])
useEffect(() => {
console.log(rect.value)
}, [rect.watcher])
// 当需要手动控制 rect 的刷新时机时,把 watcher 替换成 refresh
// 在合适的时机更新 refresh 的任意值即可
useEffect(() => {
console.log(rect.value)
}, [rect.refresh])