React 快速迁移 Vue3 模块

众所周知,在开发中效率是很重要的能力体现,很多老板都是只看结果不问过程的,作为一个出色的搬砖工人,我们也常常秉持能复制就粘贴,能借鉴就不创作的开发理念。今天,让我们来看下如何在 react 项目中快速迁移 vue3 模块。

react 和 vue3 本身就有很多相似的地方,总体上他们的逻辑的差不多的,无非是几个 api 的切换。

useEffect & watch & watchEffect

这个应该就不用多说了,直接一键替换。需要注意下 watchEffect 需要自己拾取出需要监听的依赖项

注:下面的 useUpdate 和 useCreation 都是 aHook 的钩子。可以移步这里了解其用法

ahooks - React Hooks Library - ahooks 3.0

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])
相关推荐
甜味弥漫18 小时前
JavaScript新手必看系列之预编译
前端·javascript
小哀218 小时前
🌸 入职写了一个月全栈next.js 感想
前端·后端·ai编程
用户0102692718618 小时前
swift的inout的用法
前端
用户66006766853918 小时前
搞懂作用域链与闭包:JS底层逻辑变简单
前端·javascript
yinuo18 小时前
前端跨页面通讯终极指南②:BroadcastChannel 用法全解析
前端
没落英雄18 小时前
简单了解 with
前端·javascript
越努力越幸运50818 小时前
webpack的学习打包工具
前端·学习·webpack
IT古董18 小时前
微前端的新纪元:Vite + Module Federation 最强指南(2025 全面技术解析)
前端
小小弯_Shelby18 小时前
vue项目源码泄露漏洞修复
前端·javascript·vue.js
兔子零102418 小时前
CSS 视口单位进化论:从 100vh 的「骗局」到 dvh 的救赎
前端·css