重写了vue3 ref函数 实现功能
首先vue3用的es6 module语法,导出ref,和shallowRef函数
function ref(value) {
return createRef(value, false);
}
function shallowRef(value) {
return createRef(value, true);
}
此函数可以判断传入的值是否是ref响应值
function isRef(r) {
return Boolean(r && r.__v_isRef === true);
}
通过传入shallow的值为true或者为false来决定是否需要设置为浅ref,进入函数之前先判断
传入的value是否是ref值,如果是的话就直接返回原始值。
function createRef(rawValue, shallow) {
if (isRef(rawValue)) {
return rawValue;
}
return new RefImpl(rawValue, shallow);
}
这个函数是为了防止传入的参数为reactive对象,假设我们第一次传入的
值就是普通的类型数据 observed["__v_raw"] 它的值就会是undefined,raw值为false直接返回原始值,而传入
一个reactive值时它的值就会是它的原始值
setup(){
let testobj = reactive({
a:1
})
onMounted(()=>{
console.log(testobj["__v_raw"],'testobj["__v_raw"]')
})
}
我们通过APP.vue创建一个reactive对象,取它的 __v_raw 属性可以得到它的原始值
控制台输出 ------ {a: 1} "testobj[\"__v_raw\"]"
这个时候raw 值为true 然后重新执行toRaw()函数,最终取到原始值并且返回
function toRaw(observed) {
const raw = observed && observed["__v_raw" /* RAW */];
return raw ? toRaw(raw) : observed;
}
此函数用了箭头函数的形式,适用于判断传入的 val是否为对象
相当于 function isObject(val){
if(val !== null && typeof val === Object){
return true
}
else{
return false
}
}
const isObject = (val) => val !== null && typeof val === 'object';
函数判断val是否为对象,如果是对象交给reactive实例处理,这样的话我们后面在说,假设传入
的值为原始类型,就会被直接返回,这样我们类的构造函数初始化就完成了
const convert = (val) => isObject(val) ? reactive(val) : val;
程序定义shouldTrack 为true activeEffect 为undefined
是的isTracking函数的返回值为false
let shouldTrack = true;
let activeEffect;
function isTracking() {
return shouldTrack && activeEffect !== undefined;
}
当返回值为false时,trackRefValue()函数中的代码都不会被执行
所以直接跳过。
此函数传入的是实例化后的对象,
function trackRefValue(ref) {
if (isTracking()) {
ref = toRaw(ref);
if (!ref.dep) {
ref.dep = createDep();
}
{
trackEffects(ref.dep, {
target: ref,
type: "get" /* GET */,
key: 'value'
});
}
}
}
定义了一个类来实例化ref对象,构造函数中 dep,__v_isRef分别设置初始值为 undefined,true
这个__v_isRef 属性的设置相当于标志了传入的value已经被ref实例化过了,防止再一次被实例化
导致程序出现错误,
class RefImpl {
constructor(value, _shallow) {
this._shallow = _shallow; //ref时 _shallow为false shallowRef时 _shallow 为true
this.dep = undefined;
this.__v_isRef = true;
this._rawValue = _shallow ? value : toRaw(value); //转27行toRaw()函数
this._value = _shallow ? value : convert(value); //转行60行convert()函数
}
get value() {
trackRefValue(this); //转到72行 trackRefValue()函数 ,此函数在初始条件下不执行
return this._value; 把原始值返回,这样就完成了获取ref值的操作
}
/*
class P{
get value(){
return 1
}
}
var x = new P()
console.log(x.value) // 控制台输出为1
*/
/*
class P{
value(){
return 1
}
}
var x = new P()
console.log(x.value) // 控制台输出为 [Function: value]
console.log(x.value()) //可以通过这样调用, 控制台输出为 1
*/
****在 Class 内部可以使用get和set关键字, 对某个属性设置存值函数和取值函数, 拦截该属性的存取行为。
从这里我们就能理解为什么vue3在setup函数使用ref值时要加上 .value才能取到原始值,因为这样相当于调用了
get 函数取值 当然我们也可以把value()函数值改名,
class P{
get val(){
return 1
}
}
var x = new P()
console.log(x.val)
这样我们同样也可以取到值,控制台输出为1
当函数调用set关键字存值时,首先判断如果为shallowRef时,传入的值为true
newVal值没有发生改变,而为ref时,传入的值为false,调用toRaw(),还是为了判断
值是否是reactive对象,并且如果是,就转换为原始值,
const hasChanged = (value, oldValue) => !Object.is(value, oldValue);
function triggerRefValue(ref, newVal) {
ref = toRaw(ref);
这里的话我们在构造函数中声明this.dep = undefined
所以这一段代码在这里暂时不会执行
if (ref.dep) {
{
triggerEffects(ref.dep, {
target: ref,
type: "set" /* SET */,
key: 'value',
newValue: newVal
});
}
}
}
set value(newVal) {
newVal = this._shallow ? newVal : toRaw(newVal);
//判断新值newVal 和 构造函数this._rawValue值是否相同,
不相同就直接把值赋给this._rawValue
this._value 同样要做判断,如果是对象要交给reactive()函数处理
if (hasChanged(newVal, this._rawValue)) {
this._rawValue = newVal;
this._value = this._shallow ? newVal : convert(newVal);
triggerRefValue(this, newVal);
}
}
}
exports.ref = ref;
exports.shallowRef = shallowRef;