//输入
const {ref} = VueReactivity;
const counter = ref(0);
console.log(counter.value);
counter.value = 1;
第一个函数:
function ref(value) {
return createRef(value);
}
可以看到返回的是调用createRef(0)之后的结果,接下来看看createRef函数。
function createRef(rawValue, shallow = false) {
if (isRef(rawValue)) {
return rawValue;
}
return new RefImpl(rawValue, shallow);
}
这里shallow默认为false,整个函数的作用是判断rawValue是不是Ref,如果是,直接返回,否则创建新的Ref。
如何判断传进去的值是不是已经是ref,函数如下:
function isRef(r) {
return Boolean(r && r.__v_isRef === true);
}
可以看出来是通过传进去的值的__v_isRef属性,因为之前输入传的是0,0不是对象,显然返回结果为不是Ref。
接下来看看new RefImpl发生了什么。
class RefImpl {
constructor(_rawValue, _shallow = false) {
this._rawValue = _rawValue;
this._shallow = _shallow;
this.__v_isRef = true;
this._value = _shallow ? _rawValue : convert(_rawValue);
}
get value() { ... }
set value(newVal) { ... }
}
RefImpl是一个类,当实例化的时候自动调用constructor内的代码,其他都好理解,这里看下convert函数干了什么。
const convert = val => (isObject(val) ? reactive(val) : val)
可以看出该函数作用是判断我们输入的值(0)是不是一个对象,是的话将对象变为响应式的,否则不做改动。
打印看下new Reflmpl之后最终结果。
和预期的一致,接下来看下get value里面作了什么(因为之前输入打印了counter.value,所以get value里面的代码都会执行)。
get value() {
track(toRaw(this), 'get' /* GET */, 'value');
return this._value;
}
先看toRaw。
function toRaw(observed) {
return (observed && toRaw(observed['__v_raw' /* RAW */])) || observed;
}
这里用到了递归,但是因为我们传入的是普通类型0,所以并不复杂,结果没有做任何变化,返回this,再看看track。
let shouldTrack = true;
...
let activeEffect;
...
function track(target, type, key) {
if (!shouldTrack || activeEffect === undefined) {
return;
}
...
}
这里第一个判断语句直接返回,从这里就可以看出ref对普通类型做的处理很少。
梳理一下整个过程,调用ref传0进去,会生成一个Reflmpl对象,对象内部分别挂在了__v_isRef: true,_rawValue: 1,_shallow: false,_value: 1。_rawValue和_value的值相等。
使用对象.value时返回对象._value,其他什么也不做。很简单,对吧。
接下来看看给.value赋值的时候会发生什么(之前counter.value = 1会触发set内的代码)。
set value(newVal) {
if (hasChanged(toRaw(newVal), this._rawValue)) {
this._rawValue = newVal;
this._value = this._shallow ? newVal : convert(newVal);
trigger(toRaw(this), 'set' /* SET */, 'value', newVal);
}
}
分别看下toRaw和hasChanged函数。
function toRaw(observed) {
return (observed && toRaw(observed['__v_raw' /* RAW */])) || observed;
}
const hasChanged = (value, oldValue) => {
return value !== oldValue && (value === value || oldValue === oldValue);
}
可以看出toRaw对简单类型1没有做任何改动,hasChanged比较新值1和旧值0返回true,表示值是被改变了。
更新_rawValue和_value的值(对于简单类型直接是新值)。接下来看最后的trigger发生了什么。
const targetMap = new WeakMap();
...
function trigger(target, type, key, newValue, oldValue, oldTarget) {
const depsMap = targetMap.get(target);
if (!depsMap) {
// never been tracked
return;
}
...
}
trigger函数又臭又长,看起来很复杂,但实际上用到部分不多(再次感叹对简单类型的处理)。这里因为targetMap里面没有数据直接return。
总结一下,对于简单数据类型的处理,ref做的全部工作。实例化一个RefImpl对象,分别挂载__v_isRef: true,_rawValue: 变量,_shallow: false,_value: 变量 几个属性,_rawValue和_value相等。使用对象的value时,触发get,不做任何处理直接返回_value。给对象的value重新赋值时,_rawValue和_value的值同时更新(同样两个值相同)。
简化一下整个过程:一个对象,使用对象的value,重新赋值对象的value。
结语:
好像又学到了点什么。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!