首页 > 语言 > JavaScript > 正文

详解Vue 如何监听Array的变化

2024-05-06 15:38:23
字体:
来源:转载
供稿:网友

回忆

在上一篇Vue响应式原理-理解Observer、Dep、Watcher简单讲解了Observer、Dep、Watcher三者的关系。

在Observer的伪代码中我们模拟了如下代码:

class Observer { constructor() {  // 响应式绑定数据通过方法  observe(this.data); }}export function observe (data) { const keys = Object.keys(data); for (let i = 0; i < keys.length; i++) {  // 将data中我们定义的每个属性进行响应式绑定  defineReactive(obj, keys[i]); }}export function defineReactive () { // ...省略 Object.defineProperty get-set}

今天我们就进一步了解Observer里还做了什么事。

Array的变化如何监听?

data 中的数据如果是一个数组怎么办?我们发现Object.defineProperty对数组进行响应式化是有缺陷的。

虽然我们可以监听到索引的改变。

function defineReactive (obj, key, val) {  Object.defineProperty(obj, key, {    enumerable: true,    configurable: true,    get: () => {      console.log('我被读了,我要不要做点什么好?');      return val;    },    set: newVal => {      if (val === newVal) {        return;      }      val = newVal;      console.log("数据被改变了,我要渲染到页面上去!");    }  })}let data = [1];// 对数组key进行监听defineReactive(data, 0, 1);console.log(data[0]); // 我被读了,我要不要做点什么好?data[0] = 2; // 数据被改变了,我要渲染到页面上去!

但是defineProperty不能检测到数组长度的变化,准确的说是通过改变length而增加的长度不能监测到。这种情况无法触发任何改变。

data.length = 0; // 控制台没有任何输出

而且监听数组所有索引的的代价也比较高,综合一些其他因素,Vue用了另一个方案来处理。

首先我们的observe需要改造一下,单独加一个数组的处理。

// 将data中我们定义的每个属性进行响应式绑定export function observe (data) {  const keys = Object.keys(data);  for (let i = 0; i < keys.length; i++) {    // 如果是数组    if (Array.isArray(keys[i])) {      observeArray(keys[i]);    } else {      // 如果是对象      defineReactive(obj, keys[i]);    }  }}// 数组的处理export function observeArray () {  // ...省略}

那接下来我们就应该考虑下Array变化如何监听?

Vue 中对这个数组问题的解决方案非常的简单粗暴,就是对能够改变数组的方法做了一些手脚。

我们知道,改变数组的方法有很多,举个例子比如说push方法吧。push存在Array.prototype上的,如果我们能
能拦截到原型上的push方法,是不是就可以做一些事情呢?

Object.defineProperty

对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。存取描述符是由getter-setter函数对描述的属性,也就是我们用来给对象做响应式绑定的。Object.defineProperty-MDN

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表

图片精选