likes
comments
collection
share

vue3源码学习(二)简单的依赖收集和派发更新

作者站长头像
站长
· 阅读数 28

一、 原理

vue的模板本质上是个render函数,在初始化的过程中收集render函数中的响应式对象,并在响应式对象属性值发生变化时,重新执行render函数

二、实现

  • 依赖收集:立即执行一个函数,将该函数收集到函数中涉及到的所有响应式对象中
  • 派发更新:修改响应式对象的属性值时,执行收集在该响应式对象中所有的函数

定义一个依赖类,用于存储依赖和派发更新

let activeEffect // 建一个全局变量,用于存储当前正在收集依赖的 effect 函数

/**
 *  定义一个依赖类,用于存储依赖和派发更新
 */
class Dep {
  constructor() { 
    this.effects = new Set()
  }
  /**
   * 依赖收集
   */
  depend() {
    if (activeEffect) {
      this.effects.add(activeEffect)
    }
  }
  /**
   * 派发更新
   */
  notify() {
    this.effects.forEach((_effect) => {
      _effect()
    })
  }
}

定义一个创建响应式对象的函数

  • 在读取响应式对象属性时,触发依赖收集
  • 在修改响应式对象属性时,派发更新
/**
 *  创建响应式对象的函数
 * @param {Object} obj
 */
function reactive(obj) {
  const proxy = new Proxy(obj, {
    get(target, prop) {
      const result = Reflect.get(target, prop, proxy)
      // 依赖收集
      track(target, prop)
      return result
    },
    set(target, prop, value) {
      const result = Reflect.set(target, prop, value, proxy)
      // 派发更新
      trigger(target, prop)
      return result
    },
  })

  return proxy
}

实现依赖收集(track)和派发更新(trigger)

// 所有依赖集合
const targetMap = new WeakMap()

// 收集依赖
function track(target, prop) {
  let depMap = targetMap.get(target)
  if (!depMap) {
    depMap = new Map()
    targetMap.set(target, depMap)
  }
  let dep = depMap.get(prop)
  if (!dep) {
    dep = new Dep()
    depMap.set(prop, dep)
  }
  dep.depend()
}

// 派发更新
function trigger(target, prop) {
  const depMap = targetMap.get(target)
  const dep = depMap.get(prop)
  if (dep) {
    dep.notify()
  }
}

定义一个 effect 函数,用于收集依赖

  • 传入一个函数,设置为当前正在收集依赖的 effect 函数。
  • 执行该函数,执行函数时,会触发函数中所有响应式对象get 函数 ,从而将当前 effect 函数收集到当前响应式对象的依赖集合里。
  • 将当前正在收集依赖的 effect 函数置为null
/**
 * 定义一个 effect 函数,用于收集依赖
 * @param {function} fn
 */
function effect(fn) {
  activeEffect = fn
  fn()
  activeEffect = null
}

使用示例

// 定义响应式对象
const person = reactive({
  name: '张三',
  age: 18,
})
// 依赖收集
effect(() => {
  console.log(`person -> ${person.name} -> ${person.age}`)
})
effect(() => {
  console.log(`${person.name} -> ${person.age} -> person`)
})
// 修改响应式变量属性的值,派发更新
person.name = '李四'
person.age = 20

完整代码

let activeEffect // 建一个全局变量,用于存储当前正在收集依赖的 effect 函数

/**
 *  定义一个依赖类,用于存储依赖和派发更新
 */
class Dep {
  constructor() {
    this.effects = new Set()
  }
  /**
   * 依赖收集
   */
  depend() {
    if (activeEffect) {
      this.effects.add(activeEffect)
    }
  }
  /**
   * 派发更新
   */
  notify() {
    this.effects.forEach((_effect) => {
      _effect()
    })
  }
}

// 所有依赖集合
const targetMap = new WeakMap()

// 收集依赖
function track(target, prop) {
  let depMap = targetMap.get(target)
  if (!depMap) {
    depMap = new Map()
    targetMap.set(target, depMap)
  }
  let dep = depMap.get(prop)
  if (!dep) {
    dep = new Dep()
    depMap.set(prop, dep)
  }
  dep.depend()
}

// 派发更新
function trigger(target, prop) {
  const depMap = targetMap.get(target)
  const dep = depMap.get(prop)
  if (dep) {
    dep.notify()
  }
}

/**
 *  创建一个响应式对象
 * @param {Object} obj
 */
function reactive(obj) {
  const proxy = new Proxy(obj, {
    get(target, prop) {
      const result = Reflect.get(target, prop, proxy)
      track(target, prop)
      return result
    },
    set(target, prop, value) {
      const result = Reflect.set(target, prop, value, proxy)
      trigger(target, prop)
      return result
    },
  })

  return proxy
}

/**
 * 定义一个 effect 函数,用于收集依赖
 * @param {function} fn
 */
function effect(fn) {
  activeEffect = fn
  fn()
  activeEffect = null
}

// 使用示例
const person = reactive({
  name: '张三',
  age: 18,
})
effect(() => {
  console.log(`person -> ${person.name} -> ${person.age}`)
})
effect(() => {
  console.log(`${person.name} -> ${person.age} -> person`)
})
person.name = '李四'
person.age = 20

转载自:https://juejin.cn/post/7352079327117377548
评论
请登录