完全掌握 Vue 中的 $set 方法

这篇具有很好参考价值的文章主要介绍了完全掌握 Vue 中的 $set 方法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

start

  • 今天在使用 $set 的时候,发现如果 被赋值的数据 层级较深会出现报错的情况。

  • 一知半解,是我最讨厌的状态,今天就带着问题,再阅读一下对应的源码,了解问题的本质。

问题说明

简单说明一下我遇到的问题,明确探究问题的目标。

需求

我有一个空对象,我希望可以给它的属性的属性的属性赋值。

错误代码:

<template>
  <div>
    lazy_tomato

    <h2>{{ obj }}</h2>

    <button @click="handleChange">点击我给obj赋值</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      obj: {},
    }
  },
  methods: {
    handleChange() {
      this.obj.a = {
        b: {
          c: '爱吃番茄',
        },
      }

      console.log(JSON.stringify(this.obj))
      // 直接新增属性,不会触发 vue2本质的Object.defineProperty。所以数据更新了视图不更新
    },
  },
}
</script>

vue $set,vue,vue.js,javascript,前端

正确代码

<template>
  <div>
    lazy_tomato

    <h2>{{ obj }}</h2>

    <button @click="handleChange">点击我给obj赋值</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      obj: {},
    }
  },
  methods: {
    handleChange() {
      // 错误代码二  typeError: Cannot read properties of undefined (reading '__ob__')
      // this.$set(this.obj.a, 'b.c', '爱吃番茄')

      // 正确代码
      this.$set(this.obj, 'a', { b: { c: '爱吃番茄' } })

      console.log(JSON.stringify(this.obj))
        

    },
  },
}
</script>

所以 $set 对这三个参数分别是如何处理的?如何避免我们错误使用?

官方文档

vue $set,vue,vue.js,javascript,前端
vue $set,vue,vue.js,javascript,前端

区分 Vue.set 和 vm.$set

Vue 构造函数自身上的 setvm 实例上的 $set 是相同的函数。

解决了以下问题:

1.新增对象的属性

2.删除对象的属性

3.通过数组索引修改数据

vue $set,vue,vue.js,javascript,前端

对应源码

完整源码

export 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
}

分析源码

// 1. 接受参数类型分别为  数组/对象; 任意 ; 任意
export function set(target: Array<any> | Object, key: any, val: any): any {
  // 2. 判断第一个参数 不为 undefined null string number symbol boolean
  if (
    process.env.NODE_ENV !== 'production' &&
    (isUndef(target) || isPrimitive(target))
  ) {
    warn(
      `Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`
    )
  }

  // 3. 如果是数组,而且第二个参数是有效索引
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    // 更新数组长度 有可能传入的索引大于现有索引
    target.length = Math.max(target.length, key)

    // 调用 splice
    target.splice(key, 1, val)

    // // 返回值是设置的值
    return val
  }

  // 4. 是该对象的属性 (且不是原型链上的属性)
  if (key in target && !(key in Object.prototype)) {
    // 直接赋值 (这里赋值可以触发 Object.defineProperty)
    target[key] = val

    // 返回值是设置的值
    return val
  }

  // 5. 获取 observe实例
  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
  }

  // 6. 无observe实例,直接赋值,// 返回值是设置的值
  if (!ob) {
    target[key] = val
    return val
  }

  // 7. 收集依赖
  defineReactive(ob.value, key, val)

  // 8. 手动通知,触发视图更新
  ob.dep.notify()
  // // 返回值是设置的值
  return val
}

/* 工具函数 */
function isPrimitive(value) {
  return (
    typeof value === 'string' ||
    typeof value === 'number' ||
    // $flow-disable-line
    typeof value === 'symbol' ||
    typeof value === 'boolean'
  )
}

// explicitness and function inlining.
function isUndef(v) {
  return v === undefined || v === null
}

// 是否是有效的数组索引
function isValidArrayIndex(val) {
  const n = parseFloat(String(val))
  return n >= 0 && Math.floor(n) === n && isFinite(val)
}

vue $set,vue,vue.js,javascript,前端

小结:

主要的处理顺序:

  1. 处理数组(使用 劫持过的数组 splice 方法);
  2. 处理对象上自带的属性;
  3. 收集依赖,手动触发。
// this.$set(this.obj.a, 'b.c', '爱吃番茄')
错误的原因,this.obj.a 本身是 undefined 所以直接被第一步就拦截了。

// this.obj.a={}
// this.$set(this.obj.a, 'b.c', '爱吃番茄')
也达不到效果,它会直接吧 b.c当做属性名初始化

思考:

  • 虽然官方文档设定,第二个参数是数字和字符串,理论上可以传入其他类型的。
  • 第二个参数最好是单层级的属性值

扩展 :del 方法

/**
 * Delete a property and trigger change if necessary.
 * 如果需要,删除属性并触发更改。
 */
export function del(target: Array<any> | Object, key: any) {
  if (
    process.env.NODE_ENV !== "production" &&
    // 如果是 undefined 或 null; 或者是原始值 ---同Vue.$set
    (isUndef(target) || isPrimitive(target))
  ) {
    warn(
      `Cannot delete reactive property on undefined, null, or primitive value: ${target}`
    );
  }

  // 数组,利用splice,直接改
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    target.splice(key, 1);
    return;
  }

  // ---同Vue.$set 排除Vue实例 和 根对象
  const ob = (target: any).__ob__;
  if (target._isVue || (ob && ob.vmCount)) {
    process.env.NODE_ENV !== "production" &&
      warn(
        "Avoid deleting properties on a Vue instance or its root $data " +
          "- just set it to null."
      );
    return;
  }

  // 如果 属性不是自身的属性,直接 return
  if (!hasOwn(target, key)) {
    return;
  }

  // 删除对应的key
  delete target[key];

  // 不是响应式的不做处理(这个地方可以理解为,浅层监听的 watch,有些深层的属性不需要watch,就会走这个情况)
  if (!ob) {
    return;
  }

  // 手动触发 !! 有作者在想,直接在代码中 `.__ob__`  手动通知不就ok了? 虽然可以但是不建议这样做、
  ob.dep.notify();
}

end

上述的演示,源码查看的是 vue@2.6。 vue3中由于响应式实现原理发生了变化,所以不需要 $set 了,所以不做探究。文章来源地址https://www.toymoban.com/news/detail-782556.html

到了这里,关于完全掌握 Vue 中的 $set 方法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • vue前端开发中,通过vue.config.js配置和nginx配置,实现多个入口文件的实现方法

    由于vue为单页面项目,通过控制组件局部渲染,main.js是整个项目唯一的入口,整个项目都在一个index.html外壳中。 若项目过大,会造成单页面负载过重;同时,多页面利于模块独立部署。 如果项目中不同的页面需要不同的main.js和App.vue这样就需要配置多个入口了。 要单独将页

    2024年01月22日
    浏览(90)
  • 前端刷新页面的五种方法(含原生js、vue和react)

    1、window.history.go(0)方法 2、location.reload()方法 3、location.href=location.href方法 4、vue-router方法 5、react-router方法

    2024年02月16日
    浏览(51)
  • Vue.js 中的 $emit 和 $on 方法有什么区别?

    在 Vue.js 中,$emit 和 $on 方法是两个常用的方法,用于实现组件间的通信。它们可以让我们在一个组件中触发一个自定义事件,并在另一个组件中监听这个事件,从而实现组件间的数据传递和交互。虽然它们的名字很相似,但它们的作用和用法有所不同。本文将介绍 $emit 和

    2024年02月08日
    浏览(38)
  • 已配置vue.config.js中的proxy代理,前端本地环境仍然请求后端出现跨域报错

    已配置vue.config.js中的proxy代理,前端本地环境仍然请求后端出现跨域报错 已配置vue.config.js中的proxy代理,前端本地环境仍然请求后端出现跨域报错 控制台仍然出现cors禁止报错,接口调不通 配置proxy代理解决跨域问题的原理是: 前端工程本地环境会启动一个 express 或 koa 的

    2024年02月01日
    浏览(58)
  • Vue--》深入理解 Vue 3 导航守卫,掌握前端路由的灵魂技能!

    目录 vue3导航守卫讲解与使用 element-ui的安装与使用 配置路由和设置路径别名

    2024年02月05日
    浏览(49)
  • 中高级前端需要掌握哪些Vue底层原理

    🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_ CSDN 博客专家、23年度博客之星前端领域TOP1 🕠 牛客 高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课 签约作者、上架课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你

    2024年04月11日
    浏览(34)
  • JavaScript - 判断当前时间是否在指定区间内,例如:9:00~12:00(检查当前时间是否处于规定的两个时间段范围内),适用于 vue.js / uniapp / 微信小程序等前端项目

    例如,您想知道当前时间是否处于 9:00 ~ 12:00 时间区间内,然后根据这个判断进而实现业务逻辑。 如下示例所示, 本文提供一个函数,您只需要传入 2 个时间区间,便可得出当前时间是否在该时间区间范围内: 您可以一键复制,直接粘贴到您的项目中。 您只需要传入开始时

    2024年02月16日
    浏览(63)
  • 【vue3】13-前端路由-Vue-Router的详解: 从入门到掌握

    路由其实是网络工程中的一个术语: 在 架构一个网络 时,非常重要的两个设备就是 路由器和交换机 。 当然,目前在我们生活中 路由器 也是越来越被大家所熟知,因为我们生活中都会用到 路由器 : 事实上, 路由器 主要维护的是一个 映射表 ; 映射表 会决定数据的流向; 路由

    2024年02月09日
    浏览(50)
  • 快速掌握Vue3:速成Vue3前端开发看这篇就够啦

    1.1-Vue3的优点 Vue3支持Vue2额大多数特性。 更好的支持TypeScript。 打包大小减少41%。 初次渲染快55%,更新渲染快133%。 内存减少54%。 使用proxy代替defineProperty实现数据响应式。 重写虚拟DOM的实现和Tree-Shaking。 2.1-setup 我们可以跟以前定义data和methods,但是vue3中我们更推荐使用se

    2024年02月13日
    浏览(46)
  • Vue中$set()的使用方法

    由于 Vue 会在初始化实例时进行双向数据绑定,使用 Object.defineProperty() 对属性遍历添加 getter/setter 方法,所以属性必须在 data 对象上存在时才能进行上述过程 ,这样才能让它是响应的。如果要给对象添加新的属性,此时新属性没有进行过上述过程,不是响应式的,所以会出现

    2023年04月19日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包