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])
相关推荐
肥肠可耐的西西公主5 分钟前
前端(AJAX)学习笔记(CLASS 2):图书管理案例以及图片上传
前端·笔记·学习
大胖丫6 分钟前
vue 学习-vite api.js
开发语言·前端·javascript
孙桂月7 分钟前
ES6相关操作(2)
前端·javascript·es6
陈浩源同学8 分钟前
学习 TypeScript 栈和队列数据结构
前端·算法
我这一生如履薄冰~9 分钟前
简单封装一个websocket构造函数
前端·javascript·websocket
fangcaojushi10 分钟前
解决webpack5.54打包图片及图标的问题
前端·vue.js
海盗强10 分钟前
Webpack打包优化
前端·webpack·node.js
星之卡比*12 分钟前
前端面试题---vite和webpack的区别
前端·面试
^^为欢几何^^17 分钟前
npm、pnpm和yarn有什么区别
前端·npm·node.js
AC-PEACE39 分钟前
Vue 中 MVVM、MVC 和 MVP 模式的区别
前端·vue.js·mvc