手写 Vue3的响应式 reactive 上

1,425 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第10天,点击查看活动详情

响应式系统原理

本章的目的是通过模拟 Vue3 的响应式原理,来学习Vue3 的响应式原理

Vue.js 响应式回顾

首先我们需要先回顾一下 Vue3 的响应式

  • Proxy 对象实现属性监听
  • 多层属性嵌套,在访问属性过程中处理下一级属性
  • 默认监听动态添加的属性
  • 默认监听属性的删除操作
  • 默认监听数组索引和 length 属性
  • 可以作为独立的模块使用 好的目标明确,接下来我们要实现的就是如下的几个核心方法
  • reactive/ref/torefs/computed
  • effect
  • track
  • trigger

reactive

  • 接收一个参数,判断这参数是否是对象
  • 创建拦截器对象 handler, 设置 get/set/deleteProperty
  • 返回 Proxy 对象 好,这里我们就按照上面总结的几个小点来进行模块的实现

1.接收一个参数,判断这参数是否是对象

// 判断对象是否是对象
const isObject = val => val !== null && typeof val === 'object'
export function reactive(target) {
  // 如果不是对象的话就直接返回
  if(!isObject(target)) return target  
}

2.创建拦截器对象 handler, 设置 get/set/deleteProperty

const handler = {
    get(target, key, receiver) {
      // 收集依赖
    },
    set(target, key, value, receiver) {

    },
    deleteProperty(target, key) {

    }
}
  • get 函数设置
get(target, key, receiver) {
  const result = Reflect.get(target, key, reactive)
},

这里有一个小问题,如果返回的 result 还是一个对象怎么办呢?

优化

const convent = target => isObject(target) ? reactive(target) : target
get(target, key, receiver) {
  // 收集依赖
  console.log('get', key)
  const result = Reflect.get(target, key, reactive)
  return convent(result)
},
  • set 函数设置
 // set 方法需要返回一个布尔类型的值,表示赋值是否成功
set(target, key, value, receiver) {
  // 获取旧值的目的是为了和新值比对是否相等,如果相等就不需要任何处理
  // 如果不相等 就要调用Reflect.set 方法去重新处理这个值
  // 并且需要重新触发更新
  const oldValue = Reflect.get(target, key, receiver)
  let result = true
  if(oldValue !== value) {
    result = Reflect.set(target, key, value, receiver)
    // 触发更新
    console.log('set', key, value)
  }
  return result
},
  • deleteProperty 函数设置 我们需要先判断 target 中是否有自己的 key属性, 如果有 key 属性, 删除成功之后,需要通知更新
deleteProperty(target, key) {
  const hadKey = hasOwn(target, key)
  const result = Reflect.deleteProperty(target, key)
  if(hadKey && result){
    // 触发更新
    console.log('delete', ley)
  }
  return result
}

测试

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script type="module">
    import { reactive } from './reactivity/index.js'
    const obj = reactive({
      name: 'zs',
      age: 18
    })
    obj.name = 'lisi'
    delete obj.age
    console.log(obj)
  </script>
</body>
</html>

image.png

image.png 到这里 我们的 reactive 函数就演示完毕了,下一章就需要进行依赖收集等环节了!