前言
上一篇文章给大家介绍了reactive的原理及实现一个简单的reactive(手撕Vue源码之------reactive),这篇文章就给大家介绍一下ref
,ref
也是实现响应式的一种方法,但是它一般用于让原始类型的数据变成响应式,当然也可以是引用类型,但是一般不用,至于为什么之后会给大家说明的
思路
从vue打造好的ref
来看,它接收一个参数,可以是简单类型也可以是引用类型,然后返回的是一个对象,从获取值需要使用.value就可以看出来,ref
返回的结果是一个对象。
js
import { track, trigger } from './effect.js'
import { reactive } from './reactive.js'
export function ref(val) { // 将原始类型数据变成响应式 引用类型也可以
return createRef(val)
}
function createRef(val) {
// 判断val是否已经是响应式
if (val.__v_isRef) {
return val
}
// 将val变为响应式
return new RefIpml(val)
}
class RefIpml {
constructor(val) {
this.__v_isRef = true // 给每一个被ref操作过的属性值都添加标记
this._value = convert(val) // 一种人为约定俗成的写法,加了_的属性都默认为私有属性,不会直接拿到外面去用
}
get value() {
// 为this对象做依赖收集
track(this, 'value') // key可以随便写,就统一用value,track和trigger里的key要统一
return this._value
}
set value(newVal) {
if (newVal !== this._value) {
this._value = convert(newVal)
trigger(this, 'value') // 触发掉'value'上的所有副作用函数
}
}
}
function convert(val) {
if (typeof val !== 'object' || val === null) { // 不是对象
return val
} else {
return reactive(val)
}
}
因为Proxy只接受对象作为参数,而ref
可以把简单数据类型变成响应式,所以我们就自己打造一个RefIpml
类,这里先补充一个知识点,js中的对象天生拥有get
和set
的能力。
js
let obj = {
name: 'Tom',
get age() {
return 18
},
set age(newVal) {
console.log(newVal);
}
}
obj.age = 18
console.log(obj.age); // 相当于obj.age()
在函数前面加上get就可以直接使用对象名.函数名获取函数的返回结果,同理set也是,这样就不用每次都要去调用这个函数进行触发。
这里面还用到了我们上次在reactive中使用到的track
方法和trigger
方法,既然是响应式,那么ref
自然也需要进行依赖收集和触发,代码片段里都有注释,相信大家也能看的懂,接下来我就讲讲为什么ref
一般不用于引用类型而常用于简单数据类型。
从上面的代码也能看到,当ref
如果接受的是一个对象的话,它是直接调用了reactive
方法,把reactive
的结果返回了出来,实际上vue的源码里也是这么写的,当给ref
的参数是对象时,相当于就是在ref
里面调用reactive
,然后使用reactive
返回的结果作为.value的值,这看着就有一点点多此一举。
结语
实际上变成响应式的核心就是Proxy
方法,但是Proxy
只能接受对象作为参数,所以才不得已再多打造一个ref
用来使简单数据类型也能变成响应式数据,所以一般数据源比较多的时候就用reactive
,数据源比较少的时候就用ref
。
假如您也和我一样,在准备春招。欢迎加我微信shunwuyu,这里有几十位一心去大厂的友友可以相互鼓励,分享信息,模拟面试,共读源码,齐刷算法,手撕面经。来吧,友友们!