ref
目标
了解 Vue ,手写一个方法,实现响应式,并读懂响应式
源码
class MyRef {
constructor(value) {
this._value = value
}
// 访问器
get value() {
console.log('触发 getter 函数 访问');
return this._value
}
// 读取器
set value(newVal) {
console.log('触发 setter 函数 修改');
this._value = newVal
}
}
const c1 = new MyRef(20)
console.log("c1:", c1);
setTimeout(() => {
c1.value = 30
console.log("c1:", c1);
}, 3000);
分析
1. 响应式
我们自定义了一个 class 类,并在 class 中写入 constructor 以及 get、set 方法,get value 做依赖收集并派发更新
- 第一次读取我们定义的响应式数据时,调用的时getter 方法输出如下
- 经过修改响应式数据,我们调用 setter 方法,输出如下
2. 发布 - 订阅者模式
接下来我们了解一下发布-订阅者
模式
- 首先我们先了解一下定义:
1. 发布-订阅者 模式其实是一种对象间一对多
的依赖关系(利用消息对列),一个调度中心对应多个订阅者
2. 当一个对象的状态(state)发生改变时,所有依赖于它的对象都得到状态改变的通知
3. 订阅者(Subscriber)把自己想订阅的事件注册(Subscribe)到调度中心(Event Channel)
4. 当发布者(Publisher)发布该事件(Publish Event)到调度中心,也就是该事件触发时,由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码 - 通俗易懂理解如下:
- 我们定义了一个函数,这个函数是一个发布者的事件,订阅者会直接调用发布者的事件,两者就会产生一个耦合,如果要解耦这两者之间的关系,就需要在两者中间加一个调度中心,也就是 Event Channel(一个中介) 。
- 发布订阅者模式是一个一对多的映射关系,即一个发布者对应多个订阅者
- 通常发布订阅者模式会有一个消息队列(先进先出——array)模式,即一个array中有多个方法,最先声明定义的函数最先调用
- 先定义好一个消息队列,然后根据需要的对象去订阅,当订阅的函数的状态发生改变时,对象不在主动触发,而是被动接收
- 举例:去书店
订阅书籍(on)
,当没有货时,留下一个联系方式,一旦有了货源,店员就发射(emit)
一个消息给用户,触发消息队列(message)
去买书,然后off
- 实践:
- 用websocket做一个长连接
- addEventListener 事件绑定两个事件A和事件B,当你鼠标点击后,状态就会发生变化,状态发生变化,就要按照事件队列先后执行了事件A和事件B
3. 实现数据劫持
reactive
源码
我们都知道定义引用数据类型(object、array)都是用 reactive 去定义,当我们使用 ref 去定义时 Vue 底层会做一次转换,将 ref 定义的数据转换为 reactive ,下面是分别用 reactive、ref、shallowRef 定义的三种数据,输出结果如下:
const arr1 = reactive([1, 2, 3])
console.log('arr1: ', arr1);
const arr2 = ref([1, 2, 3])
console.log('arr2: ', arr2);
const arr3 = shallowRef([1, 2, 3])
console.log('arr3: ', arr3);
分析
使用 ref 定义的 array ,系统会转为 reactive ,通过 Vue 源码我们可以看到,在定义响应式数据时,做了一个判断,如果ref传入的是一个引用数据类型,就会将其转换为 reactive ,如果想要避免出现该情况,我们可以使用shallowRef
,但是使用shallowRef
修改时,修改的 value 是响应式,但修改 value 值里面的属性不是响应式。