面试题答案
一键面试Vue3 相较于 Vue2 在内置指令底层实现的关键改变
- 数据劫持方式:
- Vue2:使用
Object.defineProperty
进行数据劫持。它通过对对象的属性进行逐个定义访问器属性(getter 和 setter)来追踪数据变化。这种方式有一定局限性,比如无法监听数组长度变化以及对象新增属性的变化,需要通过Vue.set
等方法来手动触发响应式更新。 - Vue3:采用
Proxy
替代Object.defineProperty
。Proxy
是 ES6 提供的用于创建对象代理的构造函数,它可以直接代理整个对象,而不是像Object.defineProperty
那样逐个属性进行代理。这使得它能更全面地监听对象的变化,包括数组的直接修改长度操作和对象新增属性等,无需额外方法触发更新。
- Vue2:使用
- 虚拟 DOM 优化:
- Vue2:虚拟 DOM 对比算法采用双端比较,在节点更新时可能存在不必要的移动操作,性能消耗较大。
- Vue3:使用了更高效的静态标记(PatchFlags)。通过标记不同类型的节点变化,在更新时只对比有变化的部分,大大减少了虚拟 DOM 的对比范围,提升了更新性能。
- 组件实例化和生命周期:
- Vue2:组件实例化过程相对复杂,生命周期钩子函数在组件定义时通过选项式 API 配置。
- Vue3:在组件实例化上,基于
Proxy
对组件数据进行代理,使得实例化过程更加简洁高效。同时,Composition API 改变了生命周期钩子函数的使用方式,例如setup
函数作为组件的入口,onMounted
、onUpdated
等钩子函数在setup
中使用,使得逻辑组织更加灵活。
设计并实现复杂、可复用自定义指令的方法
- 性能优化:
- 利用静态标记:如果自定义指令操作的 DOM 部分相对稳定,可以使用类似 Vue3 静态标记的思想,在指令更新时减少不必要的 DOM 操作。例如,将指令操作的 DOM 节点设置一个唯一标识,在更新时先判断标识对应的 DOM 是否需要更新,避免无意义的重新渲染。
- 防抖和节流:对于频繁触发的指令操作,如监听滚动事件的自定义指令,可以使用防抖或节流函数。在 Vue3 中,可以在
setup
函数中引入相关函数库(如lodash
的debounce
或throttle
)来实现。例如:
import { defineComponent } from 'vue';
import { debounce } from 'lodash';
export default defineComponent({
setup() {
const handleScroll = debounce(() => {
// 具体的指令逻辑
}, 200);
return {
handleScroll
};
}
});
- 兼容性:
- Polyfill:对于
Proxy
等新特性,如果需要兼容旧浏览器,可以使用 Polyfill。例如es6 - proxy - polyfill
库,在项目构建时引入该库,确保在不支持Proxy
的浏览器中也能正常使用 Vue3 的响应式机制,进而保证自定义指令的正常运行。 - 渐进增强:编写自定义指令时,优先使用兼容性好的 API,对于新特性采用渐进增强的方式。比如在处理 DOM 操作时,先使用标准的 DOM API,再根据浏览器支持情况引入新的 CSS 特性或 JavaScript 特性。
- Polyfill:对于
- 与 Composition API 结合使用:
- 在 setup 中定义指令:在
setup
函数中,可以使用defineDirective
来定义自定义指令。例如:
- 在 setup 中定义指令:在
import { defineComponent, defineDirective } from 'vue';
export default defineComponent({
setup() {
const myDirective = {
mounted(el, binding) {
// 指令挂载到 DOM 时的逻辑
el.textContent = binding.value;
},
updated(el, binding) {
// 指令所在组件更新时的逻辑
el.textContent = binding.value;
}
};
defineDirective('my - directive', myDirective);
return {};
}
});
- 复用逻辑:通过在
setup
函数中提取可复用逻辑,将其封装成函数或组合式函数,然后在自定义指令的不同钩子函数中调用。例如:
import { defineComponent, defineDirective } from 'vue';
function handleDirectiveLogic(el, binding) {
// 复用的指令逻辑
el.style.color = binding.value;
}
export default defineComponent({
setup() {
const myDirective = {
mounted(el, binding) {
handleDirectiveLogic(el, binding);
},
updated(el, binding) {
handleDirectiveLogic(el, binding);
}
};
defineDirective('my - directive', myDirective);
return {};
}
});
通过以上方法,可以在 Vue3 中设计并实现一个满足性能优化、兼容性以及与 Composition API 良好结合的复杂、可复用自定义指令。