你知道Vue中神奇的$set是如何实现的吗?

 更新时间:2022年12月13日 15:43:13   作者:清风丶  
在日常开发中,$set的也是一个非常实用的API。但是我们知其然更要知其所以然,接下来就跟随小编一起看一下Vue中的$set是如何实现的吧

前言

在日常开发中,$set的也是一个非常实用的API,因为Vue2实现响应式的核心是利用了ES5的Object.defineProperty,当我们通过直接修改数组下标更改数组或者给对象添加新的属性,这个时候Object.defineproperty是监听不到数据的变化的,这时候大家就会用上$set,让修改的操作也实现响应,我们知其然更要知其所以然,接下来看一下Vue中的$set是如何实现的

应用场景

 let dataArr = ["item1"];
 let dataObject = {
      name: "ccs"
    };

    dataArr[2] = "item2";
    dataObject.age = 22;
//    响应失败,页面没有显示更新新增的数据

    this.$set(this.dataArr,2,'item2')
    this.$set(this.dataObject,'age',22)
//响应成功,页面显示更新新增的数据

set实现

接下来我们看一下$set在Vue中的定义

function set(target: Array<any> | Object, key: any, val: any): any {
  if (
    process.env.NODE_ENV !== "production" &&
    (isUndef(target) || isPrimitive(target))
  ) {
    warn(
      `Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`
    );
  }
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    target.length = Math.max(target.length, key);
    target.splice(key, 1, val);
    return val;
  }
  if (key in target && !(key in Object.prototype)) {
    target[key] = val;
    return val;
  }
  const ob = (target: any).__ob__;
  if (target._isVue || (ob && ob.vmCount)) {
    process.env.NODE_ENV !== "production" &&
      warn(
        "Avoid adding reactive properties to a Vue instance or its root $data " +
          "at runtime - declare it upfront in the data option."
      );
    return val;
  }
  if (!ob) {
    target[key] = val;
    return val;
  }
  defineReactive(ob.value, key, val);
  ob.dep.notify();
  return val;
}

在源码中首先判断set的目标是否是undefined和基本类型如果是undefined或基本类型就报错,因为用户不应该往undefined和基本类型中set东西,然后又判断了目标是否是数组与key是不是合法的index,合法的index是指值为大于等于0的整数,如果两个条件都成立就对目标数组调用splice方法插入或者修改数组,这里的splice不是普通的splice是王维诗里的splice,是被vue代理重写过的splice

数组实现响应

$set实现数组修改响应的方式是代理重写的数组的一部分方法,接下来我们看一下具体实现

const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)

const methodsToPatch = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]
function def(obj, key, val, enumerable) {
    Object.defineProperty(obj, key, {
        value: val,
        enumerable: !!enumerable,
        writable: true,
        configurable: true
    });
}
methodsToPatch.forEach(function (method) {
  const original = arrayProto[method]
  def(arrayMethods, method, function mutator (...args) {
    const result = original.apply(this, args)
    const ob = this.__ob__
    let inserted
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args
        break
      case 'splice':
        inserted = args.slice(2)
        break
    }
    if (inserted) ob.observeArray(inserted)
    ob.dep.notify()
    return result
  })
})

vue中代理重写的不只是splice,有push、pop、shift、unshift、splice、sort、reverse这七个方法, 首先执行了const result = original.apply(this, args)执行原本数组的方法并获取它的值,接下来判断如果是往数组中添加值就将新添加的值也实现响应式,最后一步拿到这个数组的_ob_对象_ob_里的dep进行派发更新。

想深入了解vue的响应式可以查阅往期文章面试官问你Vue2的响应式原理,你怎么答?

对象实现响应

$set中下半部分的逻辑就是用来处理对象响应的,我们接着往下看

  if (key in target && !(key in Object.prototype)) {
    target[key] = val;
    return val;
  }
  const ob = (target: any).__ob__;
  if (!ob) {
    target[key] = val;
    return val;
  }
  defineReactive(ob.value, key, val);
  ob.dep.notify();
  return val;

首先判断了属性如果在目标对象中直接return结束逻辑,因为vue只有添加目标对象中原本没有的属性时才会失去响应

例如 let obj={} obj.name='ccs',

vue在初始化的时候会将data里的所有属性都变成响应式,如果的值是对象或者数组则会new一个Observer实例储存在__ob__,想深入了解vue的响应式可以查阅往期文章面试官问你Vue2的响应式原理,你怎么答

拿到这个对象的_ob_进行判断,如果不存在就说明是未经过vue初始化的普通对象而不是响应式对象 否则就手动通过defineReactive为属性添加get方法与set方法实现响应,然后手动调用dep里的notify()发布更新。

总结

vue中$set方法对数组和对象的处理本质上的一样的,对新增的值添加响应然后手动触发派发更新。

以上就是你知道Vue中神奇的$set是如何实现的吗?的详细内容,更多关于Vue实现$set的资料请关注脚本之家其它相关文章!

相关文章

  • vue之bus总线的简单使用解读

    vue之bus总线的简单使用解读

    这篇文章主要介绍了vue之bus总线的简单使用解读,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-12-12
  • vue usePop弹窗控制器的实现

    vue usePop弹窗控制器的实现

    本文主要介绍了vue usePop弹窗控制器的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • vue+elemen实现el-tooltip在文本超出区域后浮现

    vue+elemen实现el-tooltip在文本超出区域后浮现

    el-tooltip组件本身就是悬浮提示功能,在对它进行二次封装时,实现超出的文本浮现,本文就来介绍一下vue+elemen实现el-tooltip在文本超出区域后浮现,感兴趣的可以了解一下
    2023-12-12
  • Ant Design的Table组件去除

    Ant Design的Table组件去除

    这篇文章主要介绍了Ant Design的Table组件去除“取消排序”选项操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-10-10
  • 手把手教你Vue3实现路由跳转

    手把手教你Vue3实现路由跳转

    Vue Router是Vue.js的官方路由器,它与Vue.js核心深度集成,使使用Vue.js构建单页应用程序变得轻而易举,下面这篇文章主要给大家介绍了关于Vue3实现路由跳转的相关资料,需要的朋友可以参考下
    2022-08-08
  • vue3+vue-router+vite实现动态路由的全过程

    vue3+vue-router+vite实现动态路由的全过程

    动态路由是根据不同情况实时变化的路由,在权限管理系统中,动态路由常用于根据用户角色分配不同的菜单和功能,这篇文章主要介绍了vue3+vue-router+vite实现动态路由的相关资料,需要的朋友可以参考下
    2024-10-10
  • elementui之封装下载模板和导入文件组件方式

    elementui之封装下载模板和导入文件组件方式

    这篇文章主要介绍了关于elementui之封装下载模板和导入文件组件方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-12-12
  • Element中table组件(el-table)右侧滚动条空白占位处理

    Element中table组件(el-table)右侧滚动条空白占位处理

    当我设置了max-height,就会在表格右侧出现一列空白的占位,本文主要介绍了Element中table组件(el-table)右侧滚动条空白占位处理,感兴趣的可以了解一下
    2023-09-09
  • vue2.0中组件传值的几种方式总结

    vue2.0中组件传值的几种方式总结

    这篇文章主要介绍了vue2.0中组件传值的几种方式总结,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-12-12
  • Vue项目导入导出文件功能以及导出文件后乱码问题及解决

    Vue项目导入导出文件功能以及导出文件后乱码问题及解决

    这篇文章主要介绍了Vue项目导入导出文件功能以及导出文件后乱码问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-09-09

最新评论