Vue响应式数据理解

前情提要:

数据交互的方式:

1、声明式

只需要声明在哪里where,做什么what,而无需关系如何实现how

//此map方法就是,声明式的,只告诉map要实现获取2倍,然后map具体怎么实现不关心
var  arr = [1,2,3,4];
var newArr = arr.map((item,index)=>{
    return item*2;
})

2、命令式

需要以具体的代码表达在哪里where,做什么what,以及如何实现how

//forEach就是命令式,会具体操作数组,还有如何得到结果push
var  arr = [1,2,3,4];
var newArr = [];
arr.forEach((item,index)=>{
    newArr.push(item*2);
})
声明式的理解:

1)DOM状态只是数据状态的一种映射
  2)所有的逻辑尽可能在状态的层面去进行
  3)当状态变化了,View会被框架自动更新到合理的状态

正文

Vue响应式原理

什么是响应式?

<div id="app">
    <div>Price :¥{{ price }}</div>
    <div>Total:¥{{ price * quantity }}</div>
    <div>Taxes: ¥{{ totalPriceWithTax }}</div>
    <button @click="changePrice">改变价格</button>
</div>
var app = new Vue({
  el: '#app',
  data() {
    return {
      price: 5.0,
      quantity: 2
    };
  },
  computed: {
    totalPriceWithTax() {
      return this.price * this.quantity * 1.03;
    }
  },
  methods: {
    changePrice() {
      this.price = 10;
    }
  }
})

上例中当price 发生变化的时候,Vue就知道自己需要做三件事情:

  • 更新页面上price的值
  • 计算表达式 price*quantity 的值,更新页面
  • 调用totalPriceWithTax 函数,更新页面

数据发生变化后,会重新对页面渲染,这就是Vue响应式,那么这一切是怎么做到的呢?

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

Vue响应式基本概念:

Vue 的响应式,是指当数据改变后,Vue 会通知到使用该数据的代码;例如,视图渲染中使用了数据,数据改变后,视图也会自动更新。

  • 其核心机制是 观察者模式,我们把依赖数据的观察者称为 watcher;
  • 数据可以有多个观察者,怎么记录这种依赖关系呢?
    Vue 通过在data 和 watcher 间创建一个 dep 对象,来记录这种依赖关系;
  • dep 的结构很简单,除了唯一标识属性id,另一个属性就是用于记录所有观察者的 subs:
    • id - number
    • subs - [Watcher]

1.语法:

Object.definProperty(obj,prop,descriptor)

2.参数:
obj是要在其上定义属性的对象;
prop要定义的或要修改的属性的名称;
descriptor将被定义或修改的属性描述符;

3.数据描述:
configurable:是否可以删除属性,默认false;
enumberable:此属性是否可以被枚举,默认false;
value:该属性对应的值,默认undefined;
writable:属性的值是否可以被重写,默认false;

4.访问器描述:
getter:是一种获得属性值的方法;
setter:是一种设置属性值的方法;

数据变化的过程

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

追踪数据
在 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();
        })
    }
}

  • 在修改对象的值的时候,会触发对应的setter, setter通知之前依赖收集得到的 Dep 中的每一个Watcher,告诉它们自己的值改变了,需要重新渲染视图。这时候这些Watcher就会开始调用update来更新视图。
    -
    图解全过程
    在这里插入图片描述
    响应式注意项
  • 对于对象来说
    - 添加或者删除属性,那么这个属性是无法进入响应式系统的
    - 覆盖对象是可以进入响应式系统的
    - 解决方法可以用Vue.set()或是他的别名$set()
const app = new Vue({
        el: '#app',
        data: {}
    })
    
app.msg = 'hello' //不是响应式
app.$set(app, 'msg', 'hello') //响应式

  • 对于数组来说
    - 改变数组的长度
    - 根据索引值直接赋值
    - vue响应式原理理解:
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)

声响应式数据的理解:

1、把一个普通的js对象传给Vue实例的data选项对象
  2、Vue将遍历此对象的所有的属性,并使用Object.defineProperty把这些属性全部转换为getter/setter
  3、Vue内部会对数据进行劫持操作,进而追踪依赖,在属性被访问和修改时通知变化

参考文档:
https://www.cnblogs.com/fundebug/p/responsive-vue.html
https://blog.csdn.net/qq_44888570/article/details/114070909

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值