1. vue3源码- reactive

158 阅读3分钟

1.介绍

本内容是为了方便自己以后查阅同时让基础比较薄弱的人也能看懂vue的源码,所以采用的方式比较啰嗦,勿喷。不喜欢直接划走即可!!!

这里的代码和vue的源码不是完全一样的。但是核心的代码以及思路是一样的。当你学会当前的代码再去阅读源码会容易很多

这里为了编写文档时方便 且方便理解 我将所有的内容全部写到一个文件中了,后续会在main分支将所有代码抽离

reactive这个API核心只做了一件事 就是将对象变成代理对象,然后内部在做一些边界判断 所以还是比较容易懂的

不过在此之前你应该学会使用reactive api 官网都有介绍

2.开始编写

第一步 肯定是要创建并且导出reactive函数,然后将核心的步骤写出来

export const isObject = (value) => value !== null && typeof value === 'object'export const mutableHandlers = {
  get(target, key, receiver) {
    return Reflect.get(target, key, receiver)
  },
  set(target, key, value, receiver) {
    return Reflect.set(target, key, value, receiver)
  },
}
​
export function reactive(target) {
  // reacitve 只接收对象类型的数据
  if (!isObject(target)) return
  
  // 创建proxy代理
  return proxy = new Proxy(target,mutableHandlers)
}

第二步。我们来考略边界情况

// 情况一 obj已经被代理过一次了,不需要再次代理,否则会浪费性能
// 解决思路 创建一个缓存表 每次代理之前先去缓存中查看是否代理过 如果代理过就直接返回const obj = {
  name: 'zs',
  age: 19
}
const proxyObj1 = reactive(obj)
const proxyObj2 = reactive(obj)
​
​
// 情况二 代理 代理对象
/**
 * 解决思路 利用get 给当前被代理对象添加一个标识符 标识该对象已经被代理了
 * 具体做法
 * 当代码走到reactive函数的if (target[ReactiveFlags.IS_REACTIVE]) return target这段其实就会触发get (target[ReactiveFlags.IS_REACTIVE]主要是这部分触发的get) 这时候给原对象添加标识符即可  第一次肯定不存在这个标识符 所以会被代理
 *
 * 那么即使再代理这个代理对象 就像下面proxyObj2一样,也不会继续代理 因为target[ReactiveFlags.IS_REACTIVE]的结果是true
 */
​
​
const obj = {
  name: 'zs',
  age: 19,
}
const proxyObj1 = reactive(obj)
const proxyObj2 = reactive(proxyObj1)
​

解决方案

export const mutableHandlers = {
  get(target, key, receiver) {
    // 解决情况二
    
    if (!target[ReactiveFlags.IS_REACTIVE])
      target[ReactiveFlags.IS_REACTIVE] = true
    return Reflect.get(target, key, receiver)
  },
  set(target, key, value, receiver) {
    return Reflect.set(target, key, value, receiver)
  },
}
​
const reactiveMap = new WeakMap()
export function reactive(target) {
  // reactive 只接收对象
  if (!isObject(target)) return
​
  // 解决情况一  如果当前对象已经被代理过 无需二次代理
  const existProxy = reactiveMap.get(target)
  if (existProxy) return existProxy
  
  // 解决情况二
  if (target[ReactiveFlags.IS_REACTIVE]) return target
​
  // 创建proxy代理
  const proxy = new Proxy(target, mutableHandlers)
​
  // 解决情况一 缓存代理结果
  reactiveMap.set(target, proxy)
  return proxy
}

现在还有一个问题

// 情况三
/*
如下代码 info并不是一个代理对象那么当数据发生变化就不会自动更新 
原因是 Proxy只能浅层的代理
解决方案 使用递归代理  在get中判断key是否是对象 如果是则继续用reactive代理*/
const obj = {
  name: 'zs',
  info: {
    age: 19
  }
}
console.log(obj.info)

解决方案

export const mutableHandles = {
  get(target, key, receiver) {
    // 解决情况一
    if (!target[ReactiveFlags.IS_REACTIVE])
      target[ReactiveFlags.IS_REACTIVE] = true
​
    // 解决情况三
    if (isObject(target[key])) return reactive(target[key])
​
    return Reflect.get(target, key, receiver)
  },
}

至此 reactive api 已经开发完毕 没错就是这么简单 不要有心里压力。那么你将解锁下一章effect的编写

3.仓库地址

github.com/wscymdb/vue…