响应式数据原理
vue 2.0
Vue 在初始化数据时,会使用 Object.defineProperty 重新定义 data 中的所有属性,当页面使用对应属性时,首先会进行依赖收集(收集当前组件的 watcher)如果属性发生变化会通知相关依赖进行更新操作(发布订阅)。
如何监测数组变化
使用了函数劫持的方式,重写了数组的方法,Vue 将 data 中的数组进行了原型链重写,指向了自己定义的数组原型方法。这样当调用数组 api 时,可以通知依赖更新。如果数组中包含着引用类型,会对数组中的引用类型再次递归遍历进行监控。这样就实现了监测数组变化。
vue 3.0
Vue3.x 改用 Proxy 替代 Object.defineProperty。因为 Proxy 可以直接监听对象和数组的变化,并且有多达 13 种拦截方法。并且作为新标准将受到浏览器厂商重点持续的性能优化。
Proxy 只会代理对象的第一层,那么 Vue3 又是怎样处理这个问题的呢?
判断当前 Reflect.get 的返回值是否为 Object,如果是则再通过 reactive 方法做代理, 这样就实现了深度观测。
监测数组的时候可能触发多次 get/set,那么如何防止触发多次呢?
我们可以判断 key 是否为当前被代理对象 target 自身属性,也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行 trigger。
vue
<script>
const props = defineProps<{
count: number
}>()
/**
*
* vue响应式的本质
* vue 的数据响应式:数据变化时,依赖数据的函数重新运行
* 响应式:函数和数据进行关联
* 函数:被监控的函数
* render
* computed
* watch
* watchEffect
* 数据:函数中读取到的数据,该数据是响应式对象的某个属性
* 响应式数据
* 必须在函数中用到
*/
//
// 1 不是响应式
// const doubleCount = ref(props.count * 2)
// 2 响应式
// const doubleCount = ref(0)
// watchEffect(() => {
// console.log('watchEffect')
// doubleCount.value = props.count * 2
// })
// props.count(响应式数据) 变化 -> watchEffect -> doubleCount.value -> render
// 3
// 不是响应式
// function useDouble(count: number) {
// const doubleCount = ref(count * 2)
// watchEffect(() => {
// console.log('watchEffect')
// doubleCount.value = count * 2 // count 不是响应式数据,变化不会引起 watchEffect 执行
// })
// return doubleCount
// }
// const doubleCount = useDouble(props.count)
// 响应式
// function useDouble(props) {
// const doubleCount = ref(props.count * 2)
// watchEffect(() => {
// console.log('watchEffect')
// doubleCount.value = props.count * 2
// })
// return doubleCount
// }
// const doubleCount = useDouble(props)
// 4 响应式
const doubleCount = computed(() => props.count * 2)
// 5 不是响应式
function useDouble(count: number) {
const doubleCount = computed(() => count * 2)
return doubleCount
}
const doubleCount = useDouble(props.count)
</script>