返回

Vue 响应式原理:揭秘 Array 的特别处理

前端

响应式系统的核心 - 数组更新检测

相信初学 Vue.js 的同学一定踩过这个坑:改变数组的索引,没有触发视图更新。

例如,以下代码:

const app = new Vue({
  data() {
    return {
      todos: [
        { text: 'Learn Vue.js' },
        { text: 'Build something awesome' }
      ]
    }
  }
})

app.todos[0].text = 'Learn Svelte' // 视图不会更新

为什么会这样?Vue.js 如何检测数组更新的呢?

Vue.js 使用对象代理(Proxy) 来实现响应式系统。Proxy 是一种 JavaScript 内置的特性,允许我们拦截对象的属性访问和修改。当我们访问或修改一个对象的属性时,Proxy 会触发一个回调函数,在这个回调函数中,我们可以做一些事情,比如记录属性的变化、触发视图更新等。

在 Vue.js 中,Proxy 被用来代理 data 对象。当我们访问或修改 data 对象的属性时,Proxy 会触发一个回调函数,在这个回调函数中,Vue.js 会更新视图。

但是,Proxy 无法检测到数组的索引变化。这是因为数组不是对象,它是一种特殊的对象,叫做类数组对象(array-like object) 。类数组对象没有自己的属性,它的属性都是继承自其原型对象的。因此,当我们改变数组的索引时,Proxy 无法检测到,也就无法触发视图更新。

那么,如何解决这个问题呢?

Vue.js 提供了 Vue.set() 方法来解决这个问题。Vue.set() 方法可以手动触发数组更新检测。例如,以下代码:

const app = new Vue({
  data() {
    return {
      todos: [
        { text: 'Learn Vue.js' },
        { text: 'Build something awesome' }
      ]
    }
  }
})

app.todos[0].text = 'Learn Svelte' // 视图不会更新

Vue.set(app.todos, 0, { text: 'Learn Svelte' }) // 视图更新

使用 Vue.set() 方法后,视图就会更新了。这是因为 Vue.set() 方法内部使用了 Object.defineProperty() 方法,将数组的索引属性设置为响应式的。当我们改变数组的索引属性时,Object.defineProperty() 方法就会触发一个回调函数,在这个回调函数中,Vue.js 会更新视图。

结语

本文介绍了 Vue.js 中处理数组更新的原理,以及为什么直接改变数组索引不会触发视图更新。同时介绍了 Vue.set() 方法的使用,以及其在触发视图更新中的作用。

希望本文对您有所帮助。如果您有任何问题,请随时留言。