「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」。
试想一个这样的场景:
我们来试想下面的一个场景. 有一天你在逛掘金, 突然发现, 诶! 这个博主写的文章还不错, 我们关注一下. 这时候你就成为了这个博主的粉丝. 那么, 有一天这个博主发送了一篇新的技术文, 所有在他粉丝列表里的人包括你, 就会接收到消息, 提醒你, 你关注的博主更新了. 你就会去看他的文章, 并且帮他点赞收藏....😋
我们都知道Vue的基本流程就是: 数据一更新, 页面就跟着刷新. Vue2如此, Vue3亦是. 而上面这个场景就可以类比到Vue的流程中.
现在, 我们来看一下上面的场景中, 我们提到了几个角色:
- 博主(
数据
) - 我们(
页面
) - 掘金(
Vue
)
还有哪些动作呢?
- 关注博主(
订阅数据
) - 博主更新内容(
通知页面
) - 我们接收到了博主更新的消息(
页面刷新
)
我给场景中的角色和动作都加上了在Vue
中对应的实例和操作过程, 一般在动作这里, 大家可能喜欢用派发更新
, 依赖收集
, 来形容发布订阅的过程, 这样的词, 诚然更准确, 但不方便理解. 我相信你看完文章, 自然就明白了这两个词的意思了.
现在我们将场景切回代码中, 这是Vue中一个最基本的页面
<template>
<h1>I'm {{ blogger }}</h1>
</template>
<script>
export default {
data() {
return {
blogger: 'Link'
}
}
}
</script>
在页面首次渲染的时候, 我们会第一次用到blogger
这个数据, 我们可以把blogger
看成一个好博主. 而我们(页面
)关注了这个好博主. 那这时候我们就在博主的粉丝列表里了对吗?
ok, 那我们需要的就是把在读到blogger
这个数据的时候, 就把自己添加到关注列表里. 怎么做呢, 为了方便演示, 我将template
等价为函数, 实际上在Vue源码中也是这么做的.
- 定义一个数组代表关注列表, 再定义一个
watcher
函数代表页面.
let dep = [] // 关注列表
let blogger = 'Link'
function watcher() { // 假装这是页面...😂
console.log(`I'm ${blogger}`)
// 页面关注博主, 把自己添加到博主的关注列表里
dep.push(watcher)
}
这段代码, 显然是逻辑有问题的. 试想下, 我们在掘金中也只是点击关注按钮, 但是把我们添加到关注列表中
显然不应该由我们自己来做, 而是掘金做的事情.
我们先把博主丢到data对象
里, 然后请出我们的掘金Object.defineProperty
let dep = [] // 关注列表
+ let data = {
+ blogger: 'link'
+ }
+ function defineReactive() { // 这个函数是掘金平台
+ let val = data.blogger
+ Object.defineProperty(data, 'blogger', {
+ get: function reactiveGetter() {
+ dep.push(watcher)
+ return val
+ }
+ })
+ }
+ defineReactive() // 掘金平台运行起来
function watcher() { // 假装这是页面...😂
console.log(`I'm ${data.blogger}`)
}
watcher() // 页面运行
这样只要我们运行watcher
, 就会访问到blogger
, defineReactive
就会把watcher
添加到dep数组
里.好了, 到这里, 我们就把从看文章->关注博主->加入关注列表的事情做完了
现在我们需要考虑的就是当博主更新了, 我们怎么知道呢?
也很简单, 在defineProperty
我们加上set逻辑, 当blogger
改变了, 就重新执行一遍watcher
let dep = [] // 关注列表
let data = {
blogger: 'link'
}
function defineReactive() { // 这个函数是掘金平台
let val = data.blogger
Object.defineProperty(data, 'blogger', {
get: function reactiveGetter() {
dep.push(watcher)
return val
},
+ set: function reactiveSetter(newVal) {
+ val = newVal // 改变我们需要返回的值
+ dep.forEach(watcher => { // 通知关注列表的watcher重新执行
+ watcher()
+ })
+ }
})
}
defineReactive() // 掘金平台运行起来
function watcher() { // 假装这是页面...😂
console.log(`I'm ${data.blogger}`)
}
watcher() // 页面运行
data.blogger = "link2"
这时候, 只要blogger更新, 关注列表里的每一个页面函数watcher
都会知道, 并且第一时间更新.
好了. 到这里我们就把场景还原成了代码, 并且实现了一个简单的响应式逻辑. 当然这个逻辑只是为了让你理解原理. 真正的源码要比这个复杂很多. 不过只要原理懂了, 剩下的就好办了, 后面的文章我会完整的实现一个响应式系统, 真正硬核的还在后头哦
但是, 你也肯定发现了, 这个好像是Vue2的实现逻辑呀? 是的. 下篇文章我们来看看vue3是怎么实现响应式的.
感谢😘
如果觉得文章内容对你有帮助:
-
❤️ 欢迎关注点赞哦! 我会尽最大努力产出高质量的文章
往期: