深入详解Vue3 ref底层实现原理

 更新时间:2023年04月25日 14:57:37   作者:矢心  
随着现在vue3越来越普及,相应的面试题也多了起来。说到vue3的面试题,有一个最经典的就是对于实现ref和reactive这两个方法的底层原理,本文就来和大家简单讲讲吧

前言

随着现在vue3越来越普及,相应的面试题也多了起来

说到vue3的面试题,有一个最经典的就是ref和reactive的区别,用法上的区别很明显,大家都理解,对于实现这两个方法的底层原理网上却众说纷坛,各有说法。

其中vue3的reactive的是用Proxy实现的这一点是明确的

这里讲的就是这个ref,有说是使用Object.defineProperty实现的,有说是使用Proxy实现的,说法不一,到底哪些是正确的呢

下面说一下我自己的理解,如有误请在评论区指出!谢谢

源码解析

既然各方说法不一,那首先想到的就是直接去看vue3官方源码不就好了,看别人不如自己实际动起来

通过node_modules依赖文件找到vue中实现ref的源码

源码如下:

为方便理解,把相关涉及到的源码以及代码含义加上注释

function ref(value) { // ref方法
    return createRef(value, false);
}

function shallowRef(value) { // 浅层ref
    return createRef(value, true);
}

function createRef(rawValue, shallow) { // 创建ref
    if (isRef(rawValue)) { // 判断是否为ref
        return rawValue;
    }
    return new RefImpl(rawValue, shallow); // 返回RefImpl实例对象
}

class RefImpl {
    constructor(value, __v_isShallow) { // 值,是否浅层ref
        this.__v_isShallow = __v_isShallow;
        this.dep = undefined;
        this.__v_isRef = true;
        this._rawValue = __v_isShallow ? value : toRaw(value);
        this._value = __v_isShallow ? value : toReactive(value);  // 判断是否为浅层ref,否则调用toReactive,方法在下面
    }
    get value() { // getter方法 获取value值
        trackRefValue(this);
        return this._value;
    }
    set value(newVal) { // setter方法 设置value值
        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); // 在value值更新时进行判断是否为浅层ref,否则调用toReactive
            triggerRefValue(this, newVal);
        }
    }
}

const toReactive = (value) => isObject(value) ? reactive(value) : value; // 是否为对象,如果是则调用reactive

经过一番阅读理解,我们只需抓取关键信息即可

可以看到通过 ref(1) 的值在 RefImpl 类中为 _value ,然后使用class中的getset语法糖对该value值进行相应的操作,获取和赋值

再判断为对象时才使用reactive()方法

实践操作

从源码上看,我们看到了,使用了class的get和set来对这个value值进行操作,那么我们自己动手实践一下,看看怎么实现

这里把源码的_value的口头约定私有属性形式改为es9新增加的#value形式

class RefImpl {
    #value = '' // #value 私有属性

    constructor(value) {
        this.#value = value 
    }
    get value() {
        console.log('触发获取', this.#value)
        return this.#value
    }
    set value(newVal) {
        console.log('触发更新', newVal)
        this.#value = newVal
    }
}

function ref(value) {
    return new RefImpl(value)
}

const test = ref('我是小涛测试')

setTimeout(() => {
  test.value = '我设置了值'
}, 2000)

可以看到,这个简单的实例,也可以劫持数据的更新方便我们进行其他操作

class类的get和set是什么

到了这里,可以确定ref是使用class里的get/set进行数据劫持和更新的

而这个get/set实际是语法糖,本质是js的特性,是劫持property(属性)的一种方式

对象内分为数据属性访问器属性,访问器属性不包含数据,是一对get和set方法

Getter 属性访问器(accessor)和 Setter 属性修改器(mutator)

结论

综上所述

所以一刚开始说的使用Object.defineProperty说法并不正确,因为Object.defineProperty()可以用来给修改对象属性,然后使用到了getter/setter

Object.defineProperty()

所以使用了class的说法也并不正确,也是在对象内使用访问器属性,使用到了getter/setter这两个方法

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

Vue 将遍历此对象所有的属性,并使用Object.defineProperty把这些属性全部转为 getter/setter。

附上一张官方说法 官方链接

在对象内也可以使用访问器属性

const test = {
  _value: '',
  get value() {
      console.log('触发获取', this._value)
      return this._value
  },
  set value(newVal) {
      console.log('触发更新', newVal)
      this._value = newVal
  }
}

最后,其实简化到最后发现,都不会难以理解,所以保持探索态度,多看多学,方是正途

到此这篇关于深入详解Vue3 ref底层实现原理的文章就介绍到这了,更多相关Vue3 ref内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Vue中使用和移除总线Bus的注意事项详解

    Vue中使用和移除总线Bus的注意事项详解

    Vue中的总线Bus是一种通信机制,可用于组件间的数据传递和事件触发。使用时需要注意Bus的命名和定义、监听和触发事件的方法、移除和销毁Bus的时机和方式等问题。合理使用总线Bus可以提高组件的复用性和可维护性,但过度依赖可能会导致代码耦合和难以维护
    2023-04-04
  • Vue实现 点击显示再点击隐藏效果(点击页面空白区域也隐藏效果)

    Vue实现 点击显示再点击隐藏效果(点击页面空白区域也隐藏效果)

    这篇文章主要介绍了Vue实现 点击显示 再点击隐藏 点击页面空白区域也隐藏效果,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-01-01
  • element-ui图像组件、上传组件以及分页组件实现代码

    element-ui图像组件、上传组件以及分页组件实现代码

    工作中碰到需要多图上传,在使用element-ui解决过程中碰到一些问题,在这里分享给大家,这篇文章主要给大家介绍了关于element-ui图像组件、上传组件以及分页组件实现的相关资料,需要的朋友可以参考下
    2024-02-02
  • vue中tab选项卡的实现思路

    vue中tab选项卡的实现思路

    今天给大家分享vue中tab 选项卡的一些套路,非常不错,具有一定的参考借鉴价值,需要的朋友参考下吧
    2018-11-11
  • Vue项目中设置背景图片方法

    Vue项目中设置背景图片方法

    下面小编就为大家分享一篇Vue项目中设置背景图片方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-02-02
  • vue-cli3 项目优化之通过 node 自动生成组件模板 generate View、Component

    vue-cli3 项目优化之通过 node 自动生成组件模板 generate View、Component

    这篇文章主要介绍了vue-cli3 项目优化之通过 node 自动生成组件模板 generate View、Component的相关知识,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-04-04
  • VueJs 将接口用webpack代理到本地的方法

    VueJs 将接口用webpack代理到本地的方法

    本篇文章主要介绍了VueJs 将接口用webpack代理到本地的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-11-11
  • 基于element-ui封装表单金额输入框的方法示例

    基于element-ui封装表单金额输入框的方法示例

    这篇文章主要介绍了基于element-ui封装表单金额输入框的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • 对类Vue的MVVM前端库的实现代码

    对类Vue的MVVM前端库的实现代码

    这篇文章主要介绍了对类Vue的MVVM前端库的实现代码,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-09-09
  • vue 使用 sortable 实现 el-table 拖拽排序功能

    vue 使用 sortable 实现 el-table 拖拽排序功能

    这篇文章主要介绍了vue 使用 sortable 实现 el-table 拖拽排序功能,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12

最新评论