手摸手教你 vue 的依赖收集

const data = {
    
    
  flag: true,
  firstName: "z",
  lastName: "ym"
}

const computed = {
    
    
  fullName() {
    
    
    if (!data.flag) return "没有名字"
    return data.firstName + data.lastName
  }
}

我们模拟 vue 的 data 和 computed 属性,现在我们需要实现下边功能

  1. 当 flag 为 true 的时候,设置 firstName 或 lastName 或 flag 的值,fullName 的值重新计算
  2. 当 flag 为 false 的时候,设置 firstName 或 lastName 的值,fullName 的值都不重新计算,只有设置 flag 的时候,fullName 的值才会重新计算

先思考一下,如果是自己写如何实现?


首先我们想要修改 data 某个属性的值的时候,更新 comouted 的属性,那我们就肯定需要订阅它们的 get 和 set 方法

function observer(obj, key) {
    
    
  let value = obj[key]
  Object.defineProperty(obj, key, {
    
    
    get() {
    
    
      return value
    },
    set(newVal) {
    
    
      value = newVal
      // 在这里设置值的时候,重新调用 computed.fullName 的方法,从而更新 fullName 的值
      data.fullName = computed.fullName()
    }
  })
}

Object.keys(data).forEach(key => {
    
    
  observer(data, key)
})

这时候我们会发现一个问题

我们并不知道哪个 data 属性的改变会对应更新哪个 computed 属性的值,所以我们需要收集对应的依赖

我们首先需要先思考一下,Dep 收集什么依赖?
Dep 希望修改了某个 data 属性的时候能做一些其他的事情,所以我们需要在 set 之前就把那些事情收集起来

首先我们创建一个 Dep 类,用来收集依赖和派发依赖

class Dep {
    
    
  constructor() {
    
    
    this.subs = []
  }

  notify() {
    
    
    this.subs.forEach(sub => sub())
  }
}

然后我们现在需要做两件事情

  1. 给所有的 data 原属性都加上 Dep 实例
  2. 在 get 的时候收集依赖,在 set 的时候派发依赖

既然要在 get 的时候收集依赖,那么我们必须要知道什么时候会触发属性的 get

我们假设有一个 vue 的 template 模板

<template>
	<p>{
   
   { fullName }}</p>
</template>
  • 页面在渲染的过程中,就必定会调用 data.fullName 这个属性的 get 方法
  • 而 data.fullName = computed.fullName()
  • 调用 computed.fullName 会触发 flag,firstName, lastName 的 get
  • 那么自然在触发这些 get 的时候 他们就知道需要收集的是 data.fullName =
    computed.fullName() 这个依赖了

那么,我们得出来的完整代码如下

const data = {
    
    
  flag: true,
  firstName: 'z',
  lastName: 'ym'
}

const computed = {
    
    
  fullName() {
    
    
    if (!data.flag) return '没有任何东西'
    return data.firstName + data.lastName
  }
}

class Dep {
    
    
  constructor() {
    
    
    this.subs = []
  }

  notify() {
    
    
    this.subs.forEach(sub => sub())
    this.subs = []
  }
}

let willCollectFunc = () => {
    
    
  data.fullName = computed.fullName()
}

function observer(obj, key) {
    
    
  let value = obj[key]
  const dep = new Dep()
  Object.defineProperty(obj, key, {
    
    
    get() {
    
    
      dep.subs.push(willCollectFunc)
      return value
    },
    set(newVal) {
    
    
      value = newVal
      dep.notify()
    }
  })
}

Object.keys(data).forEach(key => {
    
    
  observer(data, key)
})

data.fullName = computed.fullName()

console.log(data.fullName)
data.firstName = "h"
console.log(data.fullName)
data.flag = false
console.log(data.fullName)
data.firstName = "z"
console.log(data.fullName)

更详细的内容可以看以前我写的文章,之前写得不好,所以写更基础一些更容易理解
https://blog.csdn.net/weixin_42335036/article/details/109286637

猜你喜欢

转载自blog.csdn.net/weixin_42335036/article/details/124206121