ref属性

本文详细解析了Vue3中ref和shallowRef的实现方式,包括如何判断值是否是ref响应式,创建ref实例,以及get和set操作。通过深入探讨内部函数如isTracking、trackRefValue、triggerRefValue等,揭示了Vue3响应式系统的运作机制。文章还介绍了如何防止传入的reactive对象导致错误,并讨论了如何通过value访问ref的原始值。
摘要由CSDN通过智能技术生成

 

重写了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;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值