小知识,大挑战!本文正在参与“程序员必备小知识”创作活动
前言
想了解Vue3的响应式是如何实现的嘛? 下面我带着大家去实现一个简易的响应式代码. 帮助大家理解Vue3响应式的原理. ok, 话不多说,下面跟着我的思路一起去看看吧
正文
响应式具体要实现的是一个什么功能呢? 那么我们可以先去找一个场景需求, 相信古惑仔应该是大部分人的童年回忆了,其中有一部只手遮天中,浩南和乌鸦竞拍一条长红,浩南出多少价,乌鸦使用比浩南多100块. 那么如果这个用代码怎么实现呢?
var haoNan = 180000 // 浩南出价18万
var wuya = haoNan + 100 // 始终比浩南多100块
看,很简单呀. 但是如果浩南又出19万,20万呢, 那么我们在浩南叫了价后,又得给乌鸦的叫价重新赋值. 怎么才能做到我设定一个规则, 规则就是比浩南多100块, 只要浩南重新报过了价, 那么我的价格也会重新报过,并且始终就比浩南多100块. 这就要用到数据响应式了. 我们用Vue3写的话,可以这样写:
import { ref, effect } from 'vue'
let haoNan = ref(180000)
let wuYa = 0
effect(() => {
wuYa = haoNan.value + 100
console.log(wuYa)
})
haoNan.value = 190000
// 180100 // 乌鸦报价
// 190100 // 乌鸦报价
ref实现
那么如果我们自己实现的话, 需要怎么做呢? 首先上面的代码,我们应该先有一个ref响应式对象.
接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象具有指向内部值的单个 property
.value
class RefImpl {
constructor (value) {
this._value = value
}
get value () {
return this._value
}
set value (newVal) {
if (newVal !== this._value) {
return newVal
}
}
}
export function ref(value) {
return new RefImpl(value)
}
ref的实现大概就是这样, 导出一个ref方法,我们在使用ref(180000)的时候,内部其实是去new了一个RefImpl的。 在RefImpl里边我们去get和set一个值value,这个是es6类里边的语法了。 监听这个value的变化, 然后还有一个_value私有的变量,作为一个oldValue去判断新老值,如果新老值不一致的时候,把newVal重新赋给_value.
- 问: 为什么
ref的响应式变量为什么使用的时候,我们需要.value了? - 答: 因为内部就是去监听这个
value值的变化呀. 上边实现了RefImpl但是也只是监听到一下变量的变化,还没有干任何事情,我们需要的是,当我们get到了数据的时候,把我们需要做的事情,作为依赖绑定进去,当他变化了的时候,我们去执行我们需要做的事情。 这样的话,我们就需要有一个Dep依赖了。
export default class Dep {
constructor () {
this.dep = new Set()
}
notice () {
this.dep.forEach((o) => o())
}
depend () {
if (window.rootEffect) {
this.dep.add(window.rootEffect)
}
}
}
依赖的代码也很简单,主要做的就是,发布和订阅,为什么用Set这个数据结构呢? 因为它可以给我们去重,保证数据的唯一性,我们就不用判断是否Set里边有没有这个值了。 depend方法里边,因为是简易的实现嘛,我这里为了省事,直接依赖的是一个全局变量window.rootEffect
const dep = new Dep()
class RefImpl {
constructor (value) {
this._value = value
}
get value () {
dep.depend()
return this._value
}
set value (newVal) {
if (newVal !== this._value) {
this._value = newVal
dep.notice()
}
return this._value
}
}
然后我们再在代码实现我们上边说的思路。 get的时候添加依赖,set的时候执行添加的所有依赖.
effect 实现
effect的实现就很简单了, 其实也算是比较巧妙的。
export const effect = function (fn) {
window.rootEffect = fn
fn()
window.rootEffect = null
}
这里我们先把我们要做的事情给赋值到window.rootEffect变量里边,比如这个
effect(() => {
wuYa = haoNan.value + 100
console.log(wuYa)
})
由于变量里边有haoNan.value,那么则会触发RefImpl里边的get,然后get里边做了一件事情,dep.depend(), 则会把依赖收集进去了。 然后我们haoNan.value = 190000的时候,又触发了set,然后执行notice发布订阅。 把之前收集的函数执行一遍。 这样的话,就完成了我们之前的想法。 只要浩南报价,乌鸦就会自动跟着报价,并且价格只高浩南100块了。
结语
代码属于简易的实现,可能还有一些情况没有考虑到。 但是基本的实现原理大概就是这样了。 好了,那么明天见咯。 如果觉得文章有一些帮助的话,可以给我点一个赞哟。 (^o^)/~