当前位置:   article > 正文

vue的响应式_什么是vue的响应式

什么是vue的响应式

vue的响应式简单的说就是数据变化,页面重新渲染。
如何更改数据?假设有以下代码:

 const vm = new Vue({
            el:"#app",
            data:{
                msg:"你长得真好看",
                arr:[1,2,3]
            }
        })
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

vm.msg = "小仙女"就可以更改了。为什么可以直接这样写呢?打印以下vm,可以看到有msg,
在这里插入图片描述
那么问题来了,为什么data会直接出现在vm实例对象中咧?
这是因为:当创建vue实例的时候,vue会将data中的成员代理给vue实例,目的是为了实现响应式,监控数据的变化,如何做到的呢?vue2中是使用Object.defineProperty,vue3.0中用proxy,具体实现,稍后下面会讨论。
接下来,想一下,有没有遇到过这样的情况:你更改了数据,但是页面不会渲染,这是为什么?下面给出答案:

  1. 更改的数据必须是存在的数据,否则不能重新渲染页面(未经过声明的),因为他监听不到;
  2. 更改的数据必须已渲染过的数据,否则从性能角度考虑,不会重新渲染页面;
  3. 对数组而言:(1)利用索引直接设置一个数组项时;(2)修改数组的长度时。
  4. 对对象而言:添加或者删除对象。

我们更改了数据后,页面会立刻重新渲染吗?
答案:当然不会,vue更新DOM的操作是异步执行的,只要侦听到数据变化,将开启一个异步队列,如果一个数据被多次变更,那么只会被推入到队列中一次,这样可以避免不必要的计算和DOM操作。
那我们怎么拿到更改后的数据呢?
答案:利用vm.$nextTickVue.nextTick,在页面重新渲染,DOM更新后,会立刻执行vm.$nextTick。这俩的区别是在于内部函数的this指向不同,vm.$nextTick内部函数的this指向Vue实例对象,Vue.nextTick内部函数的this指向window
那nextTick是怎么实现的呢?
答案:在nextTick的实现源码中,会先判断是否支持微任务,不支持后,才会执行宏任务。

 if(typeof Promise !== 'undefined') {
      // 微任务
      // 首先看一下浏览器中有没有promise
      // 因为IE浏览器中不能执行Promise
      const p = Promise.resolve();

    } else if(typeof MutationObserver !== 'undefined') {
      // 微任务
      // 突变观察
      // 监听文档中文字的变化,如果文字有变化,就会执行回调
      // vue的具体做法是:创建一个假节点,然后让这个假节点稍微改动一下,就会执行对应的函数
    } else if(typeof setImmediate !== 'undefined') {
      // 宏任务
      // 只在IE下有
    } else {
      // 宏任务
      // 如果上面都不能执行,那么则会调用setTimeout
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

所以,这样就可以看出vue的缺陷了,即:vue是同步执行栈执行完毕后,会执行异步队列。也就是说会一直等待主线程上的任务执行完毕,才能执行异步任务去渲染,如果主线程出现问题,那么页面就会卡死,后面异步不会执行,但react不会,一有空就会执行异步任务。
重点来了!!!
上面提到对数组和对象的更改有时候不会渲染页面,那如何更改才可以呢?
要如何响应式的更新数组和对象?
更改数组:

  1. 利用数组变异方法:push、pop、shift、unshift、splice、sort、reverse
  2. 利用vm.$set或者Vue.set实例方法
  3. 利用vm.$deleteVue.delete删除数组中的某一项

vm.$set是Vue.set的别名
使用方法:Vue.set(object, propertyName, value),也就是这个意思:Vue.set(要改谁,改它的什么,改成啥)

vm.$delete是Vue.delete的别名
使用方法:Vue.delete(object, target),也就是这个意思:Vue.delete(要删除谁的值,删除哪个)

更改对象:

  1. 添加利用vm.\$set或者Vue.set实例方法。
  2. 删除利用vm.$delete/Vue.delete方法。

那么vue的响应式原理是什么呢?即如何实现数据监听的?
vue2.0中是使用Object.defineProperty。注意对数组以及嵌套对象的处理。对数组是要重写原型上的数组方法,先克隆出一个,再对克隆的进行重写,然后将data对象的隐式原型指向克隆的即可。对嵌套对象的处理可以使用递归。

const data = {
    name:"小辣椒",
    obj:{
        x:1
    },
    arr:[4,5,6]
}

const arrayProto = Array.prototype;
const arrMethod = Object.create(arrayProto);//克隆一份
['push','pop','shift','unshift','sort','splice','reverse'].forEach(method=>{
    arrMethod[method] = function(){
        arrayProto[method].call(this,...arguments);
        render();
    }
})

function defineReactive(data,key,value){
    observer(value);
    Object.defineProperty(data,key,{
        get(){
            console.log("读");
            return value;
        },
        set(newVal){
            if(value === newVal){
                return newVal;
            }
            console.log("写");
            value = newVal;
            render();
        }
    })
}
function observer(data){
    if(Array.isArray(data)){
        data.__proto__ = arrMethod;//如果是数组就改变隐式原型
        return;
    }
    if(typeof data == "object"){
        for (const key in data) {
            defineReactive(data,key,data[key])
        }  
    }
  
}
function render(){
    console.log("页面渲染");
}
observer(data);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

利用Object.defineProperty实现响应式的劣势

  1. 天生就需要进行递归
  2. 监听不到数组不存在的索引的改变
  3. 监听不到数组长度的改变
  4. 监听不到对象的增删

在vue3.0中就解决了这个问题,具体实现,请看我的另外一篇博客。
MVVM原理之初始化data对象

以上就是我对vue响应式的理解,如有错误,请指正,谢谢!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/259203
推荐阅读
相关标签
  

闽ICP备14008679号