在使用ref和reactive的时候我产生了一个疑问,既然reactive的核心是proxy代理,那么ref对于简单数据类型的响应式的实现运用了什么样的逻辑,通过翻阅源码,找到了以下代码执行顺序----
首先ref作为函数入口,调用后经过createRef中的一系列处理最终返回给函数本身,这里第二个参数是添加一个是否为shallowRef的标记符,因为这两个API同样需要调用createRef
function ref (value) {
return createRef(value, false)
}
function shallowRef (value) {
return createRef(value, ture)
}
来到内部之后会先进行一个判断,判断传入的值是否为另一个ref对象,如果不是则没有__v_isRef,isRef返回值一定是false。这里也存在了一个强制类型转换的作用,因为当r的值也为false时,第9行这条表达式会成立,也就是当r为undefined、null、' ' 、0 、NaN时的返回值不为false而是它本身,所以强制转换之后,返回值一定是false。我想这么做的目的是为了避免第2行在进行判断时需要对条件进行隐式类型转换,也许先储存再读取的行为在这里会更好。
function createRef (rawValue, shallow) {
if (isRef(rawValue)) {
return rawValue
}
return new RefImpl(rawValue, shallow)
}
function isRef (r) {
return !!(r && r.__v_isRef === true)
}
如果上述判断结果不出意外的为false,就会执行第5的代码走下面的RefImp类形成一个新实例化对象最终返回给最先调用的ref。
下面代码其中7行代码判断不是shallowRef之后会调用toRaw拷贝一个value的原始值保存在实例身上,同时也保证了value是一个reactive对象时会进行还原。10行的_value是通过第9行中这个函数来判断是否为作为一个对象并需要通过reactive函数进行proxy代理,如果不是直接返回。下面函数value接收的参数是最开始使用ref函数处理响应式时传入的数据
class RefImpl {
constructor (value, __v_isShallow) {
this.__v_isShallow = __v_isShallow
this.dep = undefined
this.__v_isRef = true
//toRaw会根据判断进行递归,最后把对象原始值放在这里
this._rawValue = __v_isShallow ? value : toRaw(value)
//toReactive如果value是一个对象,修改_value会引起rawValue的改变
//const toReactive = value => (isObject(value) ? reactive(value) : value)
this._value = __v_isShallow ? value : toReactive(value)
}
get value () {
//追踪Ref的值
trackRefValue(this)
return this._value
}
set value (newVal) {
const useDirectValue =
this.__v_isShallow || isShallow(newVal) || isReadonly(newVal)
newVal = useDirectValue ? newVal : toRaw(newVal)
if (hasChanged(newVal, this._rawValue)) {
this._rawValue = newVal
this._value = useDirectValue ? newVal : toReactive(newVal)
triggerRefValue(this, newVal)
}
}
}
function toRaw (observed) {
const raw = observed && observed['__v_raw' /* ReactiveFlags.RAW */]
return raw ? toRaw(raw) : observed
}
代码初始化时和每当触发getter读取value发现已被修改时,14行的trackRefValue都会被调用(展示在下方),然后执行toRaw函数递归重新获取reactive对象的原始值,并打包成配置对象传给trackEffects重新进行Dep的初始化。
function trackRefValue (ref) {
if (shouldTrack && activeEffect) {
ref = toRaw(ref)
{
trackEffects(ref.dep || (ref.dep = createDep()), {
target: ref,
type: 'get' /* TrackOpTypes.GET */,
key: 'value'
})
}
}
}
触发setter时会判断ref的使用模式,先判断是否调用的为shallowRef,如果是则顾名思义的只对浅层数据进行修改,比如newVal为一个对象则会浅拷贝把内存地址直接赋给_rawValue,而不是通过toRaw递归返回一个深拷贝的对象。然后isReadOnly判断是否只读,而这个属性值会在写computed计算属性时默认加上,也可以人为调用readonly这个API标记只读不可修改。然后通过调用hasChanged使用Object.is方法判断新值是否跟_rawValue的原始值相等,如果两个值不相等,false取反后为true,就给_rawValue和_value重新赋值,且_value会多一层判断,看新值是不是对象需要通过reactive来处理
function isReadonly (value) {
return !!(value && value['__v_isReadonly' /* ReactiveFlags.IS_READONLY */])
}
const hasChanged = (value, oldValue) => !Object.is(value, oldValue)
const toReactive = value => (isObject(value) ? reactive(value) : value)
最后,setter在赋值完成之后会调用一个与getter中trackRefValue相似的函数并调用对应的Effects函数传递一个对象进行Dep修改相关的操作
//getter
function trackRefValue (ref) {
if (shouldTrack && activeEffect) {
ref = toRaw(ref)
{
trackEffects(ref.dep || (ref.dep = createDep()), {
target: ref,
type: 'get' /* TrackOpTypes.GET */,
key: 'value'
})
}
}
}
//setter
function triggerRefValue (ref, newVal) {
ref = toRaw(ref)
console.log('hh')
if (ref.dep) {
{
triggerEffects(ref.dep, {
target: ref,
type: 'set' /* TriggerOpTypes.SET */,
key: 'value',
newValue: newVal
})
}
}
}