赞
踩
vue的响应式简单的说就是数据变化,页面重新渲染。
如何更改数据?假设有以下代码:
const vm = new Vue({
el:"#app",
data:{
msg:"你长得真好看",
arr:[1,2,3]
}
})
用vm.msg = "小仙女"
就可以更改了。为什么可以直接这样写呢?打印以下vm,可以看到有msg,
那么问题来了,为什么data会直接出现在vm实例对象中咧?
这是因为:当创建vue实例的时候,vue会将data中的成员代理给vue实例,目的是为了实现响应式,监控数据的变化,如何做到的呢?vue2中是使用Object.defineProperty
,vue3.0中用proxy
,具体实现,稍后下面会讨论。
接下来,想一下,有没有遇到过这样的情况:你更改了数据,但是页面不会渲染,这是为什么?下面给出答案:
我们更改了数据后,页面会立刻重新渲染吗?
答案:当然不会,vue更新DOM的操作是异步执行的,只要侦听到数据变化,将开启一个异步队列,如果一个数据被多次变更,那么只会被推入到队列中一次,这样可以避免不必要的计算和DOM操作。
那我们怎么拿到更改后的数据呢?
答案:利用vm.$nextTick
或Vue.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 }
所以,这样就可以看出vue的缺陷了,即:vue是同步执行栈执行完毕后,会执行异步队列。也就是说会一直等待主线程上的任务执行完毕,才能执行异步任务去渲染,如果主线程出现问题,那么页面就会卡死,后面异步不会执行,但react不会,一有空就会执行异步任务。
重点来了!!!
上面提到对数组和对象的更改有时候不会渲染页面,那如何更改才可以呢?
要如何响应式的更新数组和对象?
更改数组:
push、pop、shift、unshift、splice、sort、reverse
。vm.$set
或者Vue.set
实例方法vm.$delete
或Vue.delete
删除数组中的某一项vm.$set是Vue.set的别名
使用方法:Vue.set(object, propertyName, value),也就是这个意思:Vue.set(要改谁,改它的什么,改成啥)
vm.$delete是Vue.delete的别名
使用方法:Vue.delete(object, target),也就是这个意思:Vue.delete(要删除谁的值,删除哪个)
更改对象:
vm.\$set
或者Vue.set
实例方法。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);
利用Object.defineProperty
实现响应式的劣势
在vue3.0中就解决了这个问题,具体实现,请看我的另外一篇博客。
MVVM原理之初始化data对象
以上就是我对vue响应式的理解,如有错误,请指正,谢谢!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。