Vue监测测数据(数据劫持)原理
1. Vue监视数据的原理
所谓数据劫持:将data中的数据发生变化可被检测到的技术,这也是Vue实现data变化,捕获变化同时更新模板,实现数据响应式的基础。
- 特点:Vue会监视data中所有层次的数据。
- 如何监测对象中的数据? 通过setter实现监视,且要在new Vue时就传入要监测的数据。
对象中追加的属性,Vue默认不做响应式处理
2. $set和.set添加响应式数据
- 如需给后添加的属性做响应式,请使用如下API:
Vue.set(target, propertyName/index. value)
vm.$set(target, propertyName/index. value)
特别注意
Vue.set() 和vm.$set() 不能给vm添加根属性,否则直接报错!!!
3.如何监测数组中的数据
分析:通过包裹数组更新元素的方法实现,本质就是做了2件事:
- 调用原生对应的方法对数组进行更新
- 重新解析模版,进行更新页面
Vue修改数组中的某元素一定要用到如下方法: - 数组数据变更API: push()、pop()、shift()、unshift()、splice()、sort()、reverse()
- Vue.set() 或者 vm.$set()
提示
对数组操作的push()、pop()、shift()、unshift()、splice()、sort()、reverse()实际调用的是Vue的push()、pop()、shift()、unshift()、splice()、sort()、reverse(),使用了包装模式思想,Vue在自己对应的方法内部调用了不仅数组的方法,而且实现了响应式的功能。
4.模拟实现Vue动态监测数据(setter)
- 如果使用setInterval
js
let data = {
name:"jack",
age: 18
}
let temp = 'jack'
setInterval(()=>{
if(data.name!=tmp){
tmp = data.name // 避免修改一次重复进入,将临时变量修改为最新值
console.log("name被修改了")
}
}, 100)
它看起来好用,但有两个弊端,如果要监测其他属性比如name.age, 需要额外编码,第二个弊端是setInterval
无法保证执行的时机,比如100ms后不一定会执行。这也是Vue没有使用定时器的原因。
备注:
因为我们的浏览器事件循环(Event Loop) 的机制,定时器将我们的任务放入到异步队列中执行,只有当主线程上的任务执行完成后才回去检查异步队列的任务是否需要执行。异步队列中又分为宏任务和微任务,setInterval
的回调被放置宏任务队列之中,浏览器需要优先执行微任务队列中的任务后才能执行宏任务队列。因此 setInterval
实际执行的时间是不可靠的,所在在执行动画的过程因为执行时间不精确的问题出现空帧和重复渲染的情况。
- 直接使用Object.defineProperty 如果简单粗暴使用:
直接堆栈溢出,而且data.name赋值也会堆栈溢出,原因在于getter和setter里面不断地getter和setter.肯定这么干不得行。
- 使用数据代理和Object.defineProperty
js
let data = {
name:"jack",
age: 18
}
const obs = new Observer(data);
let vm = {}
vm._data = data = obs
function Observer(obj){
data.keys().forEach((k) =>{
// 此时this指向obs
Object.defineProperty(this, 'name', {
get(){
return obj[k]
},
set(val){
console.log(`${k}被修改了,我要解析模版,生成虚拟DOM。。。。`)
obj[k] = val
}
})
})
}
实现了数据动态监测的功能,但相比之下没有Vue的完善, Vue能够监测data里面无穷级属性,并且这里改数据只能vm._data.xxx=xxx, 而vm可以vm.xxx=xxx。