实现 reactive 函数

未匹配的标注

第 1 步

packages/reactivity/src/reactiev.ts

import { isObject } from "@vue/shared";

const mutableHandlers:ProxyHandler<Record<any, any>> = {
  // recevier 表示代理对象的本身,即 createdReactiveObject 函数中的 proxy
  get(target, key, recevier) {
    // 只是相当于 target[key],并不是等于
    // 使用 Reflect 主要是他会对一些边界条件做一些处理,如取不到该属性,同时设置值的时候,还会自动返回 true 或者 false,同时,他还可以处理 this 问题
    const res = Reflect.get(target, key, recevier)
    return res
  },
  set(target, key, value, recevier) {
    // 相当于 target[key] = value,同时会返回 true
    return Reflect.set(target, key, value, recevier)
  }
}

function createdReactiveObject(target: object) {
  // 判断是否是个对象,reactive 只能对对象做处理
  if (!isObject(target)) {
    return target
  }

  const proxy = new Proxy(target, mutableHandlers)
  return proxy
}

export function reactive(target: object) {
  return createdReactiveObject(target)
}

packages/reactivity/src/index.ts

export { reactive } from './reactive'

packages/reactivity/example/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script src="../dist/reactivity.global.js"></script>
<script>
  const {reactive, effect} = VueReactivity
  const obj = {
    name: 'xiaoming',
    age: 12
  }
  const state = reactive(obj)

  console.log(state);
</script>
</body>
</html>

效果如下:

reactive 函数

第 2 步

第 1 步的问题:当同一个对象被代理两次的时候,两次代理的结果是不相同的,所以需要对代理对象进行缓存
packages/reactivity/src/reactiev.ts

import { isObject } from "@vue/shared"

const mutableHandlers:ProxyHandler<Record<any, any>> = {
  // recevier 表示代理对象的本身,即 createdReactiveObject 函数中的 proxy
  get(target, key, recevier) {
    // 等价于 target[key]
    // 使用 Reflect 主要是他会对一些边界条件做一些处理,如取不到该属性,同时设置值的时候,还会自动返回 true 或者 false
    const res = Reflect.get(target, key, recevier)
    return res
  },
  set(target, key, value, recevier) {
    // 等价于 target[key] = value,同时会返回 true
    return Reflect.set(target, key, value, recevier)
  }
}

// --------------------------新增 start-----------------------------
// 当对同一个对象进行两次代理的时候,两次的结果应该是相同的,所以这里需要保存一下代理的对象
const reactiveMap = new WeakMap()
// --------------------------新增 end-------------------------------

function createdReactiveObject(target: object) {

  // 判断是否是个对象,reactive 只能对对象做处理
  if (!isObject(target)) {
    return target
  }

  // --------------------------新增 start-----------------------------
  // 如果当前对象已经被代理过,则直接返回,不需要再进行代理
  const existingProxy = reactiveMap.get(target)
  if (existingProxy) {
    return existingProxy
  }
  // --------------------------新增 end-------------------------------

  const proxy = new Proxy(target, mutableHandlers)
  // --------------------------新增 start-----------------------------
  // 保存当前代理的对象
  reactiveMap.set(target, proxy)
  // --------------------------新增 end-------------------------------

  return proxy
}

export function reactive(target: object) {
  return createdReactiveObject(target)
}

packages/reactivity/example/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script src="../dist/reactivity.global.js"></script>
<script>
  const {reactive, effect} = VueReactivity
  const obj = {
    name: 'xiaoming',
    age: 12
  }
  const state1 = reactive(obj)
  const state2 = reactive(obj)

  console.log(state1 === state2) // true

</script>
</body>
</html>

效果如下:

reactive 函数

第 3 步

第 2 步的问题:当对一个代理对象再次代理后,和第一次代理的结果不相等,但应该是相等的,所以需要添加一个标识来判断是否是代理对象。
这里和第 2 步解决的问题是不一样的,第 2 步解决的是一个普通对象被重复代理的问题,第 3 步解决的是一个代理对象被再次代理的问题

packages/reactivity/src/reactiev.ts

import { isObject } from "@vue/shared"

// --------------------------新增 start-----------------------------
const enum ReactiveFlags {
  IS_REACTIVE = '__v_isReactive'
}
// --------------------------新增 end-------------------------------

const mutableHandlers:ProxyHandler<Record<any, any>> = {
  // recevier 表示代理对象的本身,即 createdReactiveObject 函数中的 proxy
  get(target, key, recevier) {
    // --------------------------新增 start-----------------------------
    // 判断 target 对象中是否有 __v_isReactive 属性,如果有,则表示该对象被代理过
    if (key === ReactiveFlags.IS_REACTIVE) {
      return true
    }
    // --------------------------新增 end-----------------------------

    // 等价于 target[key]
    // 使用 Reflect 主要是他会对一些边界条件做一些处理,如取不到该属性,同时设置值的时候,还会自动返回 true 或者 false
    const res = Reflect.get(target, key, recevier)
    return res
  },
  set(target, key, value, recevier) {
    // 等价于 target[key] = value,同时会返回 true
    return Reflect.set(target, key, value, recevier)
  }
}

// 当对同一个对象进行两次代理的时候,两次的结果应该是相同的,所以这里需要保存一下代理的对象
const reactiveMap = new WeakMap()

function createdReactiveObject(target: object) {
  // --------------------------新增 start-----------------------------
  // 默认认为 target 已经被代理过了
  // 此时先去 target 找 __v_isReactive 属性,如果 target 没有被代理过,那么就会返回 undefined,则会直接往下走进行代理
  if ((target as any)[ReactiveFlags.IS_REACTIVE]) {
    return target
  }
  // --------------------------新增 end-----------------------------

  // 判断是否是个对象,reactive 只能对对象做处理
  if (!isObject(target)) {
    return target
  }
  // 如果当前对象已经被代理过,则直接返回,不需要再进行代理
  const existingProxy = reactiveMap.get(target)
  if (existingProxy) {
    return existingProxy
  }

  const proxy = new Proxy(target, mutableHandlers)
  // 保存当前代理的对象
  reactiveMap.set(target, proxy)
  return proxy
}

export function reactive(target: object) {
  return createdReactiveObject(target)
}

packages/reactivity/example/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script src="../dist/reactivity.global.js"></script>
<script>
  const {reactive, effect} = VueReactivity
  const obj = {
    name: 'xiaoming',
    age: 12
  }
  const state1 = reactive(obj)
  const state2 = reactive(obj)
  console.log(reactive(state1) === state2)

</script>
</body>
</html>

效果如下:

reactive 函数

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
讨论数量: 0
发起讨论 只看当前版本


暂无话题~