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])
相关推荐
电商API&Tina9 分钟前
1688 拍立淘接口(item_search_img)测试与接入实战心得
java·大数据·前端·物联网·oracle·json
不想说话的麋鹿25 分钟前
「性能优化」虚拟列表极致优化实战:从原理到源码,打造丝滑滚动体验
前端·vue.js·面试
ouzz26 分钟前
使用 react-canvas 制作一个 Figma 工具:从画布到编辑器
前端·javascript
万少30 分钟前
AI 智能记账 Skill,基于飞书 CLI + 多维表格构建。
前端
颜酱30 分钟前
语音合成与视觉模型api接入实现
前端·javascript·人工智能
你听得到1132 分钟前
Get 这波之后,我把 Flutter 状态管理重新看了一遍:新项目到底该选谁?
前端·flutter·架构
一天睡25小时1 小时前
做产品前,先别急着写代码:我是怎么判断一个点子值不值得做的
前端
霍理迪1 小时前
TS—函数、类、泛型
前端
cc.ChenLy1 小时前
浏览器缓存机制详解:如何彻底解决前端代码更新后的缓存问题
前端
XTTX1101 小时前
Vue3+Cesium电子围栏效果
前端·javascript·vue.js