把你了解的vue响应式的原理阐述一下?
vue中有三个核心类
首先了解vue中的三个核心类
- Observer: 用来给对象的属性添加getter和setter,用来***依赖收集***和***派发更新***。
- Dep:用来收集当前响应式对象的依赖关系,每一个响应式对象都有一个dep实例,dep.subs = watcher[].是一个watcher数组。当数据发生变更的时候,会通知dep.notify()通知各个watcher。
- Watcher:观察者对象, render watcher 渲染,computed watcher 计算, user watcher监听
- 依赖收集
- initState,初始化vue组件实例的时候,对computed属性初始化时,会触发computed watcher 依赖收集。
- initState,对监听属性初始化的时候,触发的user watcher依赖收集。
- render,渲染的过程,触发render watcher依赖收集。
- 派发更新
Object.defineProperty
- 组件中对相应的数据进行了修改,或触发setter逻辑。
- dep.notify()
- 遍历所有subs,调用每一个watcher的update方法(更新视图)。
总结原理:
当创建vue实例的时候,vue先遍历data里面的属性。data初始化属性。Object.definProperty.为属性添加getter和setter对数据的读取进行劫持。
getter:依赖收集
setter:派发更新
每一个组件实例都会有对应的watcher实例。
计算属性的实现原理
data 是属性
computed : { // 都是一个个的方法,重新调用求值
test() {
{
}
computed watcher,计算属性的监听器。
computed watcher 持有一个dep实例,通过dirty属性编辑属性是否需要重新求值。
当computed的依赖值改变后,就会通知订阅的wathcer进行更新,对于computed watcher 会将 dirty 属性设置 true,并且进行计算属性方法的调用。
- computed 所谓的缓存指的是什么?
计算属性是基于他的响应式依赖进行缓存的,只有雨来大声改变时才会重新求值。
- 实际应用中怎么用的?真实项目中使用过吗?那computed缓存存在的意义是什么?或者你经常在什么时候使用?
比如计算属性的方发内部操作非常的耗时,遍历一个极大的数组,计算一次需要一秒或者更多。
const largeArray = [
{ ... },
{ ... },
{ ... }
] // 10w 条
data: {
id: 1
}
computed: {
currentItem: function() {
return largeArray.find(item => item.id === this.id);
},
stringId: function () {
return String(this.id);
}
}
- 一下情况,computed 可以监听到数据的变化吗?(比如 : 格式化转化,有缓存)
watcher 没有缓存,变化之后接下来做什么动作,调用什么函数
computed 简单的转换操作,有缓存
template
{{ storageMsg }}
computed: {
storageMsg: function(){
return sessionStorage.getItem('xxx'); // 获取不到数据,没有响应的更新上去就不会计算
},
time: function(){
return Date.now(); // 也不可以,没有响应的更新上去就不会计算
}
}
created() {
sessionStorage.setItem('xxx', 1111)
}
onClick() {
sessionStorage.setItem('xxx', Math.random())
}
vue.nextTick()
Vue.nextTick(() => {
})
await Vue.nextTick();
vue 是异步执行 dom 更新的,一旦观察到数据的变化,不会立即执行,开启异步队列,把同一个event loop 中观察数据变化的watcher推送到这个队列中。去除掉重复的事件操作。
在下一次事件循环时,vue清空异步队列,进行dom更新。
怎么样开启异步队列?
Promise.then > MutationObserver -> setImmediate > setTimeout …
vm.someData = ’ new value ’ dom 并不会立即更新,而是在异步队列被清空时才会更新dom。
里面的函数在下一轮事件队列中执行
红任务 -> 微任务队列 -> ui render(只有浏览器把元素渲染到页面,之前已经拿到新的dom,只是没有展现到浏览器上)
什么时候用到nextTick?
- 在数据变化后,要执行某个操作,而这个依赖因数据改变而改变的dom,这个操作就应该被放到vue.nextTick回调中。
<template>
<div v-if="loaded" ref="test"></div>
</template>
async showDiv() {
this.loaded = true;
// this.$refs.test // 同步获取不到test
await Vue.nextTick(); // 这个操作之后,才可以获取到test
this.$refs.test.xxx();
}