【22】 如何理解 Vue 的数据响应式

288 阅读2分钟

文章包含以下三部分:

  1. Vue 数据响应式的含义
  2. 如何做到数据响应式
  3. 如何给数据新增key

Vue 数据响应式的含义

Vue 能实现对实例中声明过的数据进行监听。当数据发生变化时,视图会根据变化内容重新渲染页面,从而简化使用者的工作。

如何做到数据响应式

通过 object.defineProperty(),配合 getter 和 setter 实现监听,同时引入代理,负责对象的属性读写

示例👇

// 需求五:就算用户擅自修改 myData,也要拦截他

let myData5 = {n:0}
let data5 = proxy2({ data:myData5 }) // 括号里是匿名对象,无法访问

function proxy2({data}{
  let value = data.n
  Object.defineProperty(data, 'n', {
    get(){
      return value
    },
    set(newValue){
      if(newValue<0)return
      value = newValue
    }
  })
  // 就加了上面几句,这几句话会监听 data

// data5 就是 obj
console.log(`需求五:${data5.n}`)
myData5.n = -1
console.log(`需求五:${data5.n},设置为 -1 失败了`)
myData5.n = 1
console.log(`需求五:${data5.n},设置为 1 成功了`)

getter 和 setter 方法

get 和 set 都是函数,当属性被访问和赋值时,会触发对应的函数

示例👇

let obj0 = {
  姓: "高",
  名: "圆圆",
  age: 18
};

// 需求一,得到姓名

let obj1 = {
  姓: "高",
  名: "圆圆",
  姓名() {
    return this.姓 + this.名;
  },
  age: 18
};

console.log("需求一:" + obj1.姓名());
// 高圆圆

// 姓名后面的括号能删掉吗?不能,因为它是函数
// 怎么去掉括号?

// 需求二,姓名不要括号也能得出值

let obj2 = {
  姓: "高",
  名: "圆圆",
  get 姓名() {
    return this.姓 + this.名;
  },
  age: 18
};

console.log("需求二:" + obj2.姓名);
//高圆圆

// 总结:getter 就是这样用的。不加括号的函数,仅此而已。

// 需求三:姓名可以被写

let obj3 = {
  姓: "刘",
  名: "诗诗",
  get 姓名() {
    return this.姓 + this.名;
  },
  set 姓名(xxx){
    this.姓 = xxx[0]
    this.名 = xxx.slice(1)
  },
  age: 18
};

obj3.姓名 = '刘诗诗'

console.log(`需求三:姓 ${obj3.姓},名 ${obj3.名}`)

// 总结:setter 就是这样用的。用 = xxx 触发 set 函数

总结

vm = new Vue({data: myData}) ,会让vm成为myData的代理,对myData的所有属性进行监控,防止myData发生变化,vm不知道。只有vm知道了每个变化,才能根据变化跟新视图,实现数据的响应式。

如何给数据新增key

一般对象新增key

使用 Vue.set 或 this.$set

methods: {
    setB() {
      Vue.set(this.obj, 'b', 1)
			// 等价于 this.$set(this.obj, 'b', 1)
    }
  }

作用

  1. 新增key
  2. 自动创建代理和监听
  3. 触发UI更新(但不会立刻更新)

以后就可以直接用传统方法修改新的变量,就不用重新set

数组对象新增key

使用 Vue 中的变异方法,之所以说是变异方法,指的是在调用这些方法时,Vue自动先执行原本的方法,再帮用户完成自动监听和代理。Vue中数组的编译方法有:

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

下面是变异的大概过程:

class VueArray extends Array{
	push(...args){
		const oldLength = this.length
		super.push(...args)
		console.log('你push了')
		for(let i = oldLength; i<this.length; i++){
			Vue.set(this, i ,this[i])
		}
	}
}

//这不是Vue的真实实现,只是展示下思路

大概做的事情是新增时,遍历了所有的项,同时加上了代理和监听,还加了一个原型层。