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])
相关推荐
lichenyang45310 分钟前
从 Express 老项目到 NestJS + Docker:一次车辆管理系统的渐进式重构
前端
Momo__1 小时前
VueUse createReusableTemplate —— 单文件组件内的模板复用神器
前端·vue.js
无名氏同学1 小时前
React 16-19 新特性
react.js
程序员小富2 小时前
我开源了一个开发者专属的智能 JSON 工具,得到了媳妇高度认可
前端·vue.js·后端
小小小小宇2 小时前
程序员如何给 LLM 装工具以及看懂推理过程
前端
写代码的皮筏艇2 小时前
React中的forwardRef
前端·react.js·面试
槑有老呆2 小时前
花三个月工资请了个 AI 程序员,结果它连青岛啤酒股价都查不了
前端
风骏时光牛马2 小时前
Verilog开发常见问题汇总解析
前端
子兮曰2 小时前
AI Coding Method Map:一张图看懂 AI 编程的完整链路
前端·人工智能·后端
不知疲倦的老鸟2 小时前
Node.js 库在浏览器里跑不了的教训
react.js·next.js