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

yizhihongxing

当你使用Vue的时候,可能会遇到一种情况:当向一个已经定义好的Vue实例中给不存在的属性赋值时,这个属性不会自动响应式地更新视图。这是因为Vue在实例化时只对已经存在的属性设置了响应式,如果后续添加了新的属性,就需要手动调用$set去设置响应式。

$set实现的原理是通过调用对象的defineReactive()方法,将新增的属性动态转换成getter/setter,并将其添加到Vue实例的响应式数据中。定义响应式数据的过程需要从Vue源码中逐步分析。

首先在Vue.js中使用了defineProperty()方法来实现数据双向绑定的。在new Vue()初始化创建实例时,会调用initState()方法为实例添加响应式数据,最终走到set()方法,执行赋值操作时会调用defineReactive()方法进行Setter的监听。

defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
return val
},
set: function reactiveSetter(newVal) {
if (newVal === val) {
return
}
val = newVal
// 触发更新
dep.notify()
}
})
}

而$set()实现的关键在于调用了Vue源码核心所在的defineProperty()方法,并将属性设置为响应式:

Vue.set(obj, 'newkey', 123)

Vue源码如下--

function set (target: Array | 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
}

举个实例:

let vm = new Vue({
  data: {
    person: {
      name: 'Tom'
    }
  }
})

vm.$set(vm.person, 'age', 20)

第一步,将vm.person传给$set(),这个person对象包含属性name。

第二步,调用Vue.set(obj, 'newkey', 123)为person对象动态添加age属性。Vue源码中判断age属性本来不存在,就会走到新添加属性的逻辑中,调用defineReactive()方法对其实现双向绑定。

第三步,这时候Vue已经将age属性转换成了getter/setter,并添加到了响应式数据中。这意味着,当不通过$set()而是直接通过person.age = 18修改age属性时,并不会触发视图更新。

所以说,当我们使用$set()方法时,$set()实际上是将数据变为响应式,即使数据是在定义实例后动态添加的。

再举一个实例:

let vmData = {
  person: {
    name: 'Tom'
  }
}

// 将vmData变成Vue实例的数据,使其响应式
let vm = new Vue({
  data: vmData 
})

// 这次不用$set,直接定义属性
vmData.person.age = 20

console.log(vmData.person.age) // 20

这里我们将vmData定义为一个普通的对象,并且在实例化Vue时将它作为响应式数据传入。接着,直接给person.age赋值,这样控制台输出的结果就是20。

这时你会发现,定义一个普通对象并将它作为响应式数据传入,这个对象的属性也会随着Vue实例的改变而改变,这是因为Vue在实例化时对所有属性都是自动进行了响应式的处理。

总之,$set() 就是为了解决Vue在实例化时无法自动新增属性并响应的问题。通过使用$set(),可以将新增的属性变成响应式的,从而触发组件的重新渲染视图。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:你知道Vue中神奇的$set是如何实现的吗? - Python技术站

(0)
上一篇 2023年5月29日
下一篇 2023年5月29日

相关文章

  • Vue中的局部组件介绍

    当我们在开发Vue应用程序时,我们通常需要将页面简化成多个模块化的组件。这个时候,我们可以使用Vue的局部组件来实现这个目的。Vue的局部组件是一种允许我们在单个Vue组件中注册私有的子组件的机制。在这个过程中,我们可以将一个Vue组件分解成多个小组件,并将这些组件放置在同一个父组件中,以更好地管理和重复使用这些组件。 如何在Vue中实现局部组件 在Vue中…

    Vue 2023年5月27日
    00
  • Vue实例初始化为渲染函数设置检查源码剖析

    首先,了解Vue实例的初始化过程非常重要。在创建Vue实例时,Vue会进行一些默认的初始化步骤,其中之一就是为渲染函数设置一些检查源码,以便在开发过程中发现错误。 具体来说,Vue会在初始化过程中调用initRender函数,该函数的主要作用是为渲染函数设置检查源码。其中,Vue会创建一个 Watcher对象并将其添加到当前Vue实例的watchers数组中…

    Vue 2023年5月28日
    00
  • vue中的计算属性和侦听属性

    当我们在使用Vue.js开发Web应用时,有时我们需要根据某些状态进行计算,计算属性和侦听属性是Vue.js为我们提供的两种响应式的计算机制。本文将详细讲解vue中的计算属性和侦听属性的完整攻略。 计算属性 什么是计算属性 计算属性指的是在模板中使用时,通过计算属性函数的返回值来得到一个新的值,这个新的值能够自动触发页面重新渲染。 计算属性的用法 计算属性的…

    Vue 2023年5月29日
    00
  • Vue2中Element DatePicker组件设置默认日期及控制日期范围

    下面是“Vue2中Element DatePicker组件设置默认日期及控制日期范围”的完整攻略。 设置默认日期 要设置Element DatePicker组件的默认日期,我们只需要在初始化时为组件的value属性指定一个默认日期即可。例如,下面的代码演示了如何设置DatePicker组件的默认日期为当前日期: <el-date-picker v-mo…

    Vue 2023年5月29日
    00
  • vue实现时间倒计时功能

    以下是“vue实现时间倒计时功能”的完整攻略,希望能对您有所帮助。 基本思路 Vue 实现时间倒计时功能的基本思路是:获取倒计时的起始时间和结束时间,然后通过 setInterval 函数计算时间差并更新视图上的倒计时剩余时间。 具体步骤 1.在Vue组件中定义起始时间和结束时间。 data() { return { startTime: new Date(…

    Vue 2023年5月28日
    00
  • Vue Router的手写实现方法实现

    让我们来详细讲解“Vue Router的手写实现方法实现”的完整攻略。 什么是Vue Router Vue Router 是Vue.js的官方路由管理器,它和Vue.js的核心深度集成,可以非常方便地实现单页应用的路由功能。 使用Vue Router可以实现以下功能: 动态路由匹配 嵌套路由 命名路由 视图过渡效果 状态管理 Vue Router手写实现 V…

    Vue 2023年5月28日
    00
  • vue3的hooks用法总结

    当谈到Vue 3中新的功能时,不得不提的是它的Hooks。Hooks是一种新的组件API,它可以让我们在函数组件中使用状态和其他React类组件中使用的功能。它在处理组件逻辑时很实用,特别是在实现可重用性和分离关注点方面。 在Vue 3中,我们可以使用若干个Hooks,包括setup、ref、reactive、watch、computed、provide、i…

    Vue 2023年5月28日
    00
  • Vue修改项目启动端口号方法

    下面是详细讲解如何修改Vue项目的启动端口号。 Vue项目启动端口号修改方法 Vue项目启动默认端口号为8080,如果该端口号冲突或者需要更改,可以按以下步骤修改: 第一步:修改package.json文件 在Vue项目根目录下找到package.json文件,将其中的scripts项中的”start”命令中的端口号修改为自己需要的端口号,例如: &quot…

    Vue 2023年5月28日
    00
合作推广
合作推广
分享本页
返回顶部