面试题答案
一键面试数据劫持
- Object.defineProperty:
- 只能对对象的已有属性进行劫持,对于新增属性需要手动调用
Vue.$set
才能使之变为响应式。例如在一个对象data = {name: 'John'}
中,如果后续添加data.age = 20
,默认不会触发视图更新。 - 劫持的是对象的属性,深度监听需要递归处理,性能消耗较大。当对象嵌套层次很深时,递归遍历每个属性并使用
Object.defineProperty
进行劫持,会增加初始化的性能开销。
- 只能对对象的已有属性进行劫持,对于新增属性需要手动调用
- Proxy:
- 可以直接对整个对象进行代理,无需关心对象属性的增减,对新增属性和删除属性都能直接监听。例如
const data = new Proxy({}, {})
,后续对该代理对象添加或删除属性都能被监听到。 - 无需递归监听,代理对象可以直接处理整个对象的操作,在处理深层嵌套对象时性能更优。比如对于
{a: {b: {c: 1}}}
这样的深层对象,使用Proxy监听更高效。
- 可以直接对整个对象进行代理,无需关心对象属性的增减,对新增属性和删除属性都能直接监听。例如
依赖收集与更新
- Object.defineProperty:
- 依赖收集的粒度是属性级别的,在数据更新时,即使只是对象中的一个小部分发生变化,也可能会导致整个组件重新渲染。例如对象
{name: 'John', age: 20}
中,name
属性变化,可能会使依赖该对象的整个组件重新渲染。 - 对于数组,通过重写数组的变异方法(如
push
、pop
等)来实现依赖收集与更新,实现相对复杂且不够灵活。
- 依赖收集的粒度是属性级别的,在数据更新时,即使只是对象中的一个小部分发生变化,也可能会导致整个组件重新渲染。例如对象
- Proxy:
- 依赖收集可以基于整个对象,能够更精准地控制更新范围,减少不必要的重新渲染。例如对象某个属性变化时,Proxy能更准确判断哪些部分依赖了该变化,从而只更新相关部分。
- 对数组的监听更加自然,不需要像
Object.defineProperty
那样重写数组方法,Proxy可以直接监听数组的变化,在处理数组相关的依赖收集和更新时更简洁高效。