Vue数据响应式

129 阅读6分钟

说说对 Vue 数据响应式的理解

什么是数据响应式

vue数据响应式:简单来说就是,UI界面会通过数据的改变而发生响应的变化。我们不用关心DOM,只要数据变化Ui界面就会变化。

getter

**get**语法将对象属性绑定到查询该属性时将被调用的函数。

语法

{get prop() { ... } }

{get [expression]() { ... } }

参数

prop
要绑定到给定函数的属性名。
expression
从 ECMAScript 2015 开始,还可以使用一个计算属性名的表达式绑定到给定的函数。

描述

有时需要允许访问返回动态计算值的属性,或者你可能需要反映内部变量的状态,而不需要使用显式方法调用。在JavaScript中,可以使用 getter 来实现。

尽管可以结合使用getter和setter来创建一个伪属性,但是不可能同时将一个 getter 绑定到一个属性并且该属性实际上具有一个值。

setter

当尝试设置属性时,**set**语法将对象属性绑定到要调用的函数。

语法

{set prop(val) { . . . }}
{set [expression](val) { . . . }}

参数

prop
要绑定到给定函数的属性名。
val
用于保存尝试分配给prop的值的变量的一个别名。
表达式
从 ECMAScript 2015 开始,还可以使用一个计算属性名的表达式绑定到给定的函数。

描述

在 javascript 中,如果试着改变一个属性的值,那么对应的 setter 将被执行。setter 经常和 getter 连用以创建一个伪属性。不可能在具有真实值的属性上同时拥有一个 setter 器。

使用 getter/setter

let obj = {
  name: "小明",
  age: 25,
  get ag() {
    console.log("姓名" + this.name + "年龄" + this.age)
  },
  set an(value) {
    this.name = value
  },
  set ag(value) {
    this.age = value
  }
}
console.log(obj)
obj.an = "小红"
obj.ag = 20
console.log(obj)

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

语法

Object.defineProperty(obj, prop, descriptor)

参数

obj
要定义属性的对象。
prop
要定义或修改的属性的名称或 Symbol 。
descriptor
要定义或修改的属性描述符。
返回值
被传递给函数的对象。

示例

let obj = {}
Object.defineProperty(obj, "name", {
  value: "小明"
})
console.log(obj)
console.log(obj.name)

Vue数据响应

当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter

这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 能够追踪依赖,在 property 被访问和修改时通知变更。

每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。

微信图片_20220212225451.png

检测变化的注意事项

由于 JavaScript 的限制,Vue 不能检测数组和对象的变化。尽管如此我们还是有一些办法来回避这些限制并保证它们的响应性。

对于对象

Vue 无法检测 property 的添加或移除。由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的。例如:

var vm = new Vue({
  data:{
    a:1
  }
})

// `vm.a` 是响应式的
vm.b = 2
// `vm.b` 是非响应式的

对于已经创建的实例,Vue 不允许动态添加根级别的响应式 property。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式 property。例如,对于:

Vue.set(vm.someObject, 'b', 2)

您还可以使用 vm.$set 实例方法,这也是全局 Vue.set 方法的别名:

this.$set(this.someObject,'b',2)

有时你可能需要为已有对象赋值多个新 property,比如使用 Object.assign()_.extend()。但是,这样添加到对象上的新 property 不会触发更新。在这种情况下,你应该用原对象与要混合进去的对象的 property 一起创建一个新的对象。

// 代替 `Object.assign(this.someObject, { a: 1, b: 2 })`
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })

对于数组

Vue 不能检测以下数组的变动:

  1. 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
  2. 当你修改数组的长度时,例如:vm.items.length = newLength

举个例子:

var vm = new Vue({
  data: {
    items: ['a', 'b', 'c']
  }
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的

为了解决第一类问题,以下两种方式都可以实现和 vm.items[indexOfItem] = newValue 相同的效果,同时也将在响应式系统内触发状态更新:

// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)

你也可以使用 vm.$set 实例方法,该方法是全局方法 Vue.set 的一个别名:

vm.$set(vm.items, indexOfItem, newValue)

为了解决第二类问题,你可以使用 splice

vm.items.splice(newLength)

声明响应式 property

由于 Vue 不允许动态添加根级响应式 property,所以你必须在初始化实例前声明所有根级响应式 property,哪怕只是一个空值:

var vm = new Vue({
  data: {
    // 声明 message 为一个空值字符串
    message: ''
  },
  template: '<div>{{ message }}</div>'
})
// 之后设置 `message`
vm.message = 'Hello!'

如果你未在 data 选项中声明 message,Vue 将警告你渲染函数正在试图访问不存在的 property。

小结

  • **Object.defineProperty() **
可以给对象添加属性value
可以给对象添加getter/setter
getter/setter用于对属性的读写进行监控
  • 设置代理(设计模式)
对象myData的读写,全权由类一个对象vm负责
那么vm就是myD阿特的代理(类比房东租房)
比如myDate不用,偏要用vm来操作myData
  • vm = new Vue({data :myData)
会让vm成为myData的代理
会对mYData的所有属性进行监控
为什么要监控,为了防止myDate属性变了,vm不知道
vm知道了又如何,知道属性变了就可以调用render(data)
UI=render(data)
  • 示意图

微信图片_20220212235213.png

  • 数据响应式

    什么是响应式

我打你一拳,你喊疼,那么你就是响应式
若一个物体能对外界的刺激做出反应,他就是响应式
  • vue的data的 响应式
const vm = new Vue({data:{n:0})
我们如果修改vm.n,那么UI中的n就会响应
vue 2 是通过Object.defineProperty()来实现数据响应式               
  • 响应式页面是啥?www.smashingmagazine.com/

  • vue实例data中的数据如歌没有实现声明,就监听不到例如

var vm = new Vue({
  data:{
    a:1
  }
})

// `vm.a` 是响应式的
vm.b = 2
// `vm.b` 是非响应式的

解决方法使用vue.set或者this.$set

  • 新增key
  • 自动创建代理和监听(如果没有创建过)
  • 触发UI更新(但并不是立即书法)
var vm = new Vue({
  data:{
    a:1
  }
})

// `vm.a` 是响应式的
//vm.b = 2
vue.set(vm.data,"b",2)
或者
vm.$set(this.data,'b',2)
//这时 `vm.b` 也是是响应式的
  • 数组怎么办,vue不能检测到数组新增的下标
var vm = new Vue({
  template: `<div>{{a.toString()}} <button @click="add">按钮</button></div>`,
  data: {
    a: [1, 2, 3]
  },
  methods: {
    add() {
      // this.a[3] = 4 //这时vue检测不a的变化
      // this.$set(this.a, '3', 4)
      //这个数组数组不是原来的push犯法,是被尤雨溪改写后的方法
      this.a.push(4)
    }
  }
}).$mount('#app')

尤雨溪 做了什么

  • 篡改数组AP,见文档中变更方法章节
  • 这7个API都被vue篡改,调用后更新UI

变更方法

Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

你可以打开控制台,然后对前面例子的 items 数组尝试调用变更方法。比如 example1.items.push({ message: 'Baz' })