reactivity响应式
本文最后更新于 106 天前,如有失效请评论区留言。

reactivity响应式

之前简单实现了dep的监听和更新,具体的在vue2和vue3中分别是使用了 Object.defineProperty Proxy作为监听,而不是只监听.value属性

首先是调用reactive方法传入一个对象,开始监听这个对象,当这个对象发生改变时,调用watchEffect传入的方法,这就是要达到的结果

const state = reactive({
  count: 0
})

watchEffect(() => {
  console.log(state.msg);
})

再接着围绕这个结果开始构建出 reactive 函数和 watchEffect

let activeEffect
function watchEffect (effect) {
  activeEffect = effect
  effect()
  activeEffect = null
}

watchEffect还是和上一篇一样,定义一个变量保存,以便收集依赖时添加进set

再把之前的收集依赖和派发更新一同拷贝过来

class Dep {
  subscribers = new Set()
  depend () {
    if (activeEffect) {
      this.subscribers.add(activeEffect)
    }
  }
  notify (effect) {
    this.subscribers.forEach(effect => {
      effect()
    })
  }
}

然后就是reactive的实现方法,在vue2中,因为兼容性,采用了Object.defineProperty,分别监听每一个对象的键值

function reactive (raw) {
  Object.keys(raw).forEach((key) => {
    const dep = new Dep()
    let value = raw[key]

    Object.defineProperty(raw, key, {
      get () {
        dep.depend()
        return value
      },
      set (newValue) {
        value = newValue
        dep.notify()
      }
    })
  })
  return raw
}

vue3中,则直接使用es6的proxy监听整个对象,更加提升效率,不用每个都去遍历,也可以监听到新增和删除的属性,还有像array这种对象不用再hook里面的七个常用方法就可以直接监听到

function reactive (raw) {
  return new Proxy(raw, reactiveHandles)
}

接着就去详细实现reactiveHandles里面的方法

const targetMap = new WeakMap()

function getDep (target, key) {
  let depsMap = targetMap.get(target)
  if (!depsMap) {
    depsMap = new Map()
    targetMap.set(target, depsMap)
  }
  let dep = depsMap.get(key)
  if (!dep) {
    dep = new Dep()
    depsMap.set(key, dep)
  }
  return dep
}
const reactiveHandles = {
  get (target, key, receiver) {
    const dep = getDep(target, key)
    dep.depend()
    // return target[key]
    return Reflect.get(target, key, receiver)
  },
  set (target, key, value, receiver) {
    const dep = getDep(target, key)
    const result = Reflect.set(target, key, value, receiver)
    dep.notify()
    return result
  }
}

首先通过get、和set方法监听取值和赋值操作,在get里添加依赖,添加收集的时候用key作为键dep对象作为值,这里添加了一个WeakMap弱引用到这个对象,当这个对象没有依赖时,便于垃圾回收清理。

在set中拿到对应key的dep,触发watchEffect里面的方法,达到响应式

set和get中调用的Reflect,可以去看这篇文章,详细的介绍了Reflect.get(target, key, receiver) 和 target[key] 的区别

这里每一个key都对应一个新的dep对象,dep里的subscribers可能会有收集到的effect函数,因为如果watchEffect传入的函数没有访问这个key,也就不会触发get收集函数

完整代码如下:

<!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>
</body>

</html>
<script>
  let activeEffect

  class Dep {
    subscribers = new Set()
    depend () {
      if (activeEffect) {
        this.subscribers.add(activeEffect)
      }
    }
    notify (effect) {
      this.subscribers.forEach(effect => {
        effect()
      })
    }
  }
  function watchEffect (effect) {
    activeEffect = effect
    effect()
    activeEffect = null
  }

  const targetMap = new WeakMap()

  function getDep (target, key) {
    let depsMap = targetMap.get(target)
    if (!depsMap) {
      depsMap = new Map()
      targetMap.set(target, depsMap)
    }
    let dep = depsMap.get(key)
    if (!dep) {
      dep = new Dep()
      depsMap.set(key, dep)
    }
    return dep
  }
  const reactiveHandles = {
    get (target, key, receiver) {
      const dep = getDep(target, key)
      dep.depend()
      // return target[key]
      return Reflect.get(target, key, receiver)
    },
    set (target, key, value, receiver) {
      const dep = getDep(target, key)
      const result = Reflect.set(target, key, value, receiver)
      dep.notify()
      return result
    }
  }

  // vue2 写法
  /* function reactive (raw) {
    Object.keys(raw).forEach((key) => {
      const dep = new Dep()
      let value = raw[key]

      Object.defineProperty(raw, key, {
        get () {
          dep.depend()
          return value
        },
        set (newValue) {
          value = newValue
          dep.notify()
        }
      })
    })
    return raw
  } */
  function reactive (raw) {
    return new Proxy(raw, reactiveHandles)
  }

  const state = reactive({
    count: 0
  })

  watchEffect(() => {
    console.log(state.msg);
  })

  state.count++
</script>
版权声明:本文为BIMiracle原创,依据CC BY-SA 4.0许可证进行授权,转载请附上出处链接及本声明。
暂无评论

发送评论 编辑评论


|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇