我们上文说了,Vue 是通过 Object.defineProperty 和重写数组的原型方法来达到监控数据的目的。但是,在某些情况下,上面两种方案无法做到监控数据的变化,例如:

(1):当我们给对象设置一个新属性的时候,obj.newProperty = xxxxx;

(2):当我们删除对象中的某个属性的时候,delete obj.oldProperty;

上面两种情况,Vue 的响应式系统都监控不到,为了弥补这两个缺陷,Vue 提供了 $set 和 $delete API,当我们想设置新的属性,或者删除某个属性的时候,不要用 js 原生的语法操作,而是使用 $set 和 $delete API 来完成任务。

这两个 API 的思路其实和重写数组的原型方法是一样的,都是对 JS 中的某些原生操作进行重写,当我们调用这些重写的方法对数据进行操作的时候,Vue 自然就能监控到我们对数据做了哪些事情,进而做相对应的处理就可以了,接下来看源码。

1,这两个 API 是如何挂载到 Vue 原型中的

1-1,首先看 src/core/instance/index.js

function Vue (options) {
// 如果当前的环境不是生产环境,并且当前命名空间中的 this 不是 Vue 的实例的话,
// 发出警告,Vue 必须通过 new Vue({}) 使用,而不是把 Vue 当做函数使用
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
// 执行 vm 原型上的 _init 方法,该方法在 initMixin 方法中定义
this._init(options)
} // 写入 vm.$set、vm.$delete、vm.$watch
stateMixin(Vue) export default Vue

挂载这两个 API 到 Vue 原型上是在 stateMixin 方法中。

1-2,src/core/instance/state.js ==> stateMixin()

import {
set,
del
} from '../observer/index' export function stateMixin (Vue: Class<Component>) {
Vue.prototype.$set = set
Vue.prototype.$delete = del
}

从上面的代码可知,$set 和 $del API 的实现定义在 ../observer/index 文件中

2,$set 的实现

Vue.set 或者说是$set 原理如下

因为响应式数据 我们给对象和数组本身都增加了__ob__属性,代表的是 Observer 实例。当给对象新增不存在的属性 首先会把新的属性进行响应式跟踪 然后会触发对象__ob__的 dep 收集到的 watcher 去更新,当修改数组索引时我们调用数组本身的 splice 方法去更新数组

/**
* vm.$set 的底层实现
*/
export function set (target: Array<any> | Object, key: any, val: any): any {
// 如果 target 是一个数组,并且 key 也是一个有效的数组索引值的话
if (Array.isArray(target) && isValidArrayIndex(key)) {
// 设置数组的 length 属性,设置的属性值是 "数组原长度" 和 "key" 中的最大值
target.length = Math.max(target.length, key)
// 然后通过数组原型上的方法,将 val 添加到数组中
// 在前面数组响应式源码的阅读中可以知道,通过数组原型的方法添加的元素,其是会被转换成响应式的
target.splice(key, 1, val)
return val
}
// 这里用于处理 key 已经存在于 target 中的情况
if (hasOwn(target, key)) {
// 由于这个 key 已经存在于对象中了,也就是说这个 key 已经被侦测了变化,在这里,只不过是修改下属性而已
// 所以在这里,直接修改属性,并返回 val 即可
target[key] = val
return val
} const ob = (target: any).__ob__ // 如果 target 没有 __ob__ 属性的话,说明 target 并不是一个响应式的对象
// 所以在这里也不需要做什么额外的处理,将 val 设到 target 上,并且返回这个 val 即可
if (!ob) {
target[key] = val
return val
}
// 如果上面所有的判断条件都不满足的话,说明用户是在响应式数据上新增了一个数据,这种情况需要跟踪这个新增属性的变化
// 在这里使用 defineReactive 将 val 变成 getter/setter 的形式
defineReactive(ob.value, key, val)
// 因为新增了一个属性,所以 ob.value 变化了,所以在这里需要出发依赖的更新
ob.dep.notify()
return val
}

  

3,$delete 的实现

Vue.set 或者说是$set 原理如下

因为响应式数据 我们给对象和数组本身都增加了__ob__属性,代表的是 Observer 实例。当给对象删除一个已经存在的属性 直接触发对象__ob__的 dep 收集到的 watcher 去更新,当修改数组索引时我们调用数组本身的 splice 方法去更新数组

/**
* Delete a property and trigger change if necessary.
*/
// Vue 对数据的监控是通过 Object.defineProperty() 实现的,所以当用户通过 delete 关键字删除某个字段时,Vue 是检测不到的,
// 为了解决这个问题,Vue 提供了 vm.$delete 来解决这个问题
export function del (target: Array<any> | Object, key: any) {
// 如果 target 是一个数组,并且 key 是一个下标值的话
if (Array.isArray(target) && isValidArrayIndex(key)) {
// 执行数组原型上的 splice 方法,该方法会执行删除的操作,并且会出发依赖的更新
target.splice(key, 1)
return
}
const ob = (target: any).__ob__
// 如果 target 上面没有 key 属性的话,直接 return 即可,什么都不用干
if (!hasOwn(target, key)) {
return
}
// 使用 js 中原生的 delete 关键字删除指定的 key
delete target[key]
// 在这里判断 target 是不是响应式的,如果不是的话,就不用出发依赖的更新操作了。在这里,直接 return
if (!ob) {
return
}
// 出发依赖的更新操作
ob.dep.notify()
}

VUE中的$set与$delete的原理的更多相关文章

  1. vue中数据双向绑定的实现原理

    vue中最常见的属v-model这个数据双向绑定了,很好奇它是如何实现的呢?尝试着用原生的JS去实现一下. 首先大致学习了解下Object.defineProperty()这个东东吧! * Objec ...

  2. 浅谈Vue中计算属性computed的实现原理

    虽然目前的技术栈已由Vue转到了React,但从之前使用Vue开发的多个项目实际经历来看还是非常愉悦的,Vue文档清晰规范,api设计简洁高效,对前端开发人员友好,上手快,甚至个人认为在很多场景使用V ...

  3. Vue中MVVM模式的双向绑定原理 和 代码的实现

      今天带大家简单的实现MVVM模式,Object.defineProperty代理(proxy)数据   MVVM的实现方式: 模板编译(Compile) 数据劫持(Observer) Object ...

  4. 理解vue中v-for循环中得key原理及一些错误

    作用:给节点做一个标识,相当于人类的身份证号,虚拟DOM中的标识 下列是key值的一些使用场景和带来的问题:   js:    const vm = new Vue({             el: ...

  5. vue中路由跳转的底层原理

    前端路由是直接找到与地址匹配的一个组件或对象并将其渲染出来.改变浏览器地址而不向服务器发出请求有两种方式: 1. 在地址中加入#以欺骗浏览器,地址的改变是由于正在进行页内导航 2. 使用H5的wind ...

  6. 通俗易懂了解Vue中nextTick的内部实现原理

    1. 前言 nextTick 是 Vue 中的一个核心功能,在 Vue 内部实现中也经常用到 nextTick.在介绍 nextTick 实现原理之前,我们有必要先了解一下这个东西到底是什么,为什么要 ...

  7. vue中的v-model原理,与组件自定义v-model

    VUE中的v-model可以实现双向绑定,但是原理是什么呢?往下看看吧 根据官方文档的解释,v-model其实是一个语法糖,它会自动的在元素或者组件上面解析为 :value="" ...

  8. 对Vue中的MVVM原理解析和实现

    对Vue中的MVVM原理解析和实现 首先你对Vue需要有一定的了解,知道MVVM.这样才能更有助于你顺利的完成下面原理的阅读学习和编写 下面由我阿巴阿巴的详细走一遍Vue中MVVM原理的实现,这篇文章 ...

  9. Vue中Object和Array数据变化侦测原理

    在学完Vue.js框架,完成了一个SPA项目后,一直想抽时间找本讲解Vue.js内部实现原理的书来看看,经过多方打听之后,我最后选择了<深入浅出Vue.js>这本书.然而惭愧的是,这本书已 ...

  10. Vue中关于数组与对象修改触发页面更新的机制与原理简析

    Vue中关于数组与对象修改触发页面更新的机制与原理简析 相关问题 数组 使用索引直接赋值与直接修改数组length时,不会触发页面更新. 例如: <script> export defau ...

随机推荐

  1. vscode python可以运行,无法debug

    参考:https://blog.csdn.net/weixin_44646187/article/details/125810974 提示'cmd' 不是内部或外部命令,也不是可运行的程序 或批处理文 ...

  2. pycharm cv2 的方法不能智能提示

    按住ctrl,光标放在cv2上,就能跳转到cv2的__init__.py文件 全选,按Ctrl+/注释掉所有语句,然后将如下语句添加到__init__.py中 import sys import os ...

  3. 内存模型和名称空间复习题(c++ prime plus )

    第一题 注意到setgolf()被重载,可以这样使用其第一个版本: golf ann; setgolf(ann, "Ann Birdfree", 24); 上述函数调用提供了存储在 ...

  4. opencv对鱼眼图像畸变矫正

    import numpy as np ''' #T_cam_imu body_T_cam0: !!opencv-matrix rows: 4 cols: 4 dt: d data: [0.003489 ...

  5. visio2019 破解

    链接:https://pan.baidu.com/s/14AU8GFXhgjp87pK1bIK8UA?pwd=ju61 提取码:ju61 --来自百度网盘超级会员V5的分享 可能需要关闭防火墙和win ...

  6. 转型.netCore开发 记录日常问题

    1.页面问题 asp-for date字段页面显示时间为"yyyy-MM-dd"类型通过@{}来处理,定义常量,将处理后的值赋值给常量,再通过@常量正常赋值例:@{ var beg ...

  7. C语言初级阶段4——数组2————二维数组

    C语言初级阶段4--数组2----二维数组 二维数组的定义:类型说明符 数组名[数组大小] [数组大小] 第一个大小是行的大小,第二个大小是列的大小. 二维数组的初始化:{} #include< ...

  8. P2504 聪明的猴子

    题目描述 在一个热带雨林中生存着一群猴子,它们以树上的果子为生.昨天下了一场大雨,现在雨过天晴,但整个雨林的地表还是被大水淹没着,部分植物的树冠露在水面上.猴子不会游泳,但跳跃能力比较强,它们仍然可以 ...

  9. OO_Lab1总结博客

    OO_Lab1 问题描述 模拟多线程实时电梯系统,新主楼ABCDE五个楼座各楼层均有电梯,乘客发起形如"从X座x层到Y座y层"的请求,电梯模拟上下行.开关门.乘客进出等行为,以满足 ...

  10. 基于Nginx以及web服务器搭建在线视频播放

    安装Nginx Nginx官网下载地址 网址打开后如图 下载windows版本的Nginx,这里下载最新的1.18.0版本 Nginx在windows下的安装只需要将其解压缩即可.建议将解压后的目录移 ...