Vue 数据响应式详解

news/2024/7/9 23:40:49 标签: vue, js, 响应式, watcher, dep

前言

Vue最具特色的一点,就是数据驱动,即你不用写复杂的DOM操作,只需专心于业务所用到的数据。
那么,Vue的数据响应式到底是怎么实现的呢?

正文

数据变化的过程

  • 侦测数据的变化 (数据劫持 / 数据代理)
  • 收集视图依赖了哪些数据 (依赖收集)
  • 数据变化时,自动“通知”需要更新的视图部分,并进行更新 (发布订阅模式)

追踪数据

new Vue() 后, Vue 会调用 _init 函数进行初始化,也就是init 过程,在 这个过程Data通过Observer转换成了getter/setter的形式,来对数据追踪变化,当被设置的对象被读取的时候会执行getter函数,而在当被赋值的时候会执行setter函数。

  • 也就是说,当创建Vue实例时,Vue会将这个实例的data对象转换成getter/setter形式,这对用户是不可见的。
  • 所以就出现了无法进入响应式系统的数据,稍后介绍。

收集依赖

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

  • 当外界通过Watcher读取数据时,会触发getter从而将Watcher添加到依赖中。

订阅者

主要作用是用来存放 Watcher观察者对象

class Dep {
    constructor () {
        /* 用来存放Watcher对象的数组 */
        this.subs = [];
    }
    /* 在subs中添加一个Watcher对象 */
    addSub (sub) {
        this.subs.push(sub);
    }
    /* 通知所有Watcher对象更新视图 */
    notify () {
        this.subs.forEach((sub) => {
            sub.update();
        })
    }
}
  • 在修改对象的值的时候,会触发对应的settersetter通知之前依赖收集得到的 Dep 中的每一个 Watcher,告诉它们自己的值改变了,需要重新渲染视图。这时候这些Watcher就会开始调用update来更新视图。
    在这里插入图片描述

图解全过程

在这里插入图片描述

响应式注意项

  • 对于对象来说
    • 添加或者删除属性,那么这个属性是无法进入响应式系统的
    • 覆盖对象是可以进入响应式系统的
    • 解决方法可以用Vue.set()或是他的别名$set()
const app = new Vue({
        el: '#app',
        data: {}
    })
    
app.msg = 'hello' //不是响应式
app.$set(app, 'msg', 'hello') //响应式
  • 对于数组来说
    • 改变数组的长度
    • 根据索引值直接赋值
const app = new Vue({
    el: '#app',
    data: {
        arr: [1, 2, 3, 4]
    }
})

app.arr[1] = 3333   //不是响应式
app.$set(app.arr, 1, 3333)  //是响应式

app.arr.length = 3   //不是响应式
app.arr.splice(3)  //是响应式
  • 声明响应式: property 由于 Vue 不允许动态添加根级响应式 property,所以你必须在初始化实例前声明所有根级响应式 property,哪怕只是一个空值
  • Vue 在更新 DOM 时是异步执行的,为了在数据变化之后等待 Vue 完成更新 DOM,可以在数据变化之后立即使用Vue.nextTick(callback)。这样回调函数将在 DOM 更新完成后被调用。
    • Vue.nextTick(callback)或实例方法app.$nextTick(callback)
    • $nextTick()返回一个Promise对象,所以你可以使用新的 ES2017async/await 语法完成
     this.title = ''

     this.$nextTick(() => {
         alert(this.title)
     })

     this.title = 123
	//alert(123)

参考文档

结语

如果对你有帮助的话,请点一个赞吧


http://www.niftyadmin.cn/n/1257948.html

相关文章

const 关键字

可能有的误解 对于 ES6 中增加的const关键字,很多人的(包括我自己)都有一些误解。这个误解是:使用const关键字定义的变量是无法被修改的,即必须在定义的时候就赋值。这话不能说是全错,只是对简单类型和引用…

then 方法

then()方法 如果不是第一个参数函数,则会忽略这个then(),例如 func() .then(hello) //这个then会被忽略 .then(res>{console.log(res) })resolve(arg)的参数arg会传到紧接着的.then(callback(arg))中如果你读懂了下面的例子,那么说明你对t…

数组和伪数组的区别

正文 常见的伪数组有:函数参数arguments,DOM对象列表(例如document.querySelectorAll(div)),jQuery中的$(div)等Array.prototype 伪数组没有Array.prototype,它只是一个对象数组有Array.prototype,他是对象的同时&…

一道经典的 js 面试题

原题目 在浏览器环境下,判断程序输出的结果是? var foo window的foolet obj {foo: obj的foo,func() {let self thisconsole.log(this.foo)console.log(self.foo);(function () {console.log(this.foo)console.log(self.foo)})()}}obj.func()console.…

深拷贝的实现

正文 Object.prototype.toString() 返回的是[object Object]表示数据 类型的字符串可以利用这一点来确定数据的具体类型和[].toString()以及Number.prototype.toString()都不一样,数组和字符串等类型数据都重写了toString()方法。 function type(val) {return {}…

手动实现一些函数的封装

正文 全局对象 node 中的 全局 this指向的是module.exports,而不是全局对象global 在外部函数中的this指向global,匿名函数也是 浏览器环境下全局this就是window 在外部函数中的this指向window,匿名函数也是 reverse - 不改变原数组版&a…

call,apply,bind 实现

正文 Tips:call bind apply对箭头函数来说,会忽略掉第一个参数,也就是this指向参数,所以不会改变箭头函数this的指向 call实现 this指向绑定传参返回值,看了很多的实现例子,都没有写这个。如果缺少返回值…

js 知识点备忘

长期更新… 前言 有太多知识点,其篇幅不值得写一篇博客,那么就把它们汇总吧,干脆做一个备忘。 如果你阅读了这篇博客,请先了解 有些知识点可能是不准确的,甚至是不正确的,因为我随时都会修改&#xff0c…