前端面试题 - Vue

180 阅读9分钟

一、什么是虚拟 DOM

  • 虚拟 DOMVirtual DOM)是一种轻量级的 JavaScript 对象,它描述了真实 DOM 中的节点信息和属性。虚拟 DOM 可以在内存中进行操作,然后通过算法比较新旧虚拟 DOM 的差异,最终只对发生变化的部分进行 DOM 操作,从而提高了性能。在 Vue.js 中,每个组件都有自己的虚拟 DOM 对象,当组件的数据发生变化时,Vue.js 会重新生成这个组件的虚拟 DOM 对象,并与旧的虚拟 DOM 对象进行比较,找出需要更新的节点,并生成一个补丁(Patch),最后将这个补丁应用到真实的 DOM 中,完成更新操作。使用虚拟 DOM 的好处是可以避免频繁地操作真实 DOM,因为真实 DOM 操作非常昂贵,对性能有很大的影响。虚拟 DOM 可以将多个操作合并为一个批量操作,从而减少了操作次数,提高了性能。

二、生命周期有哪些?分别做了什么事情?

  • onBeforeMount():这个钩子函数在组件挂载到节点上之前执行。可以在这个函数里做一些准备工作,比如数据的初始化。
  • onMounted():这个钩子函数在组件挂载完成后执行。此时,组件的 DOM 节点已经创建完成,可以用于执行需要访问组件所渲染的 DOM 树相关的操作,或在服务端渲染应用中用于确保 DOM 相关代码。
  • onBeforeUpdate():这个钩子函数在组件更新之前执行,可以在此进行更新前的准备工作。
  • onUpdated():这个钩子函数在组件更新完成之后执行,可以用于执行更新后的恢复或整理工作。
  • onBeforeUnmount():这个钩子函数在组件卸载之前执行,可以在此进行一些清理工作,比如取消事件监听、定时器等。
  • onUnmounted():这个钩子函数在组件卸载完成之后执行,可以在此执行一些收尾工作,比如内存的释放等。

三、watchcomputed 的区别?

  • 用途:computed 是计算属性,用于根据已有的数据计算出一个新的属性值;而 watch 是监听属性,用于观察和响应 Vue 实例上的数据变化。
  • 缓存:computed 支持缓存,当其依赖的属性的值发生变化时,计算属性会重新计算,反之,则使用缓存中的属性值;而 watch 不支持缓存,当对应属性发生变化的时候,会立即响应执行。
  • 异步操作:computed 不支持异步操作,有异步操作时无法监听数据变化;而 watch 支持异步操作。
  • 监听时机:computed 首次渲染时,开始进行数据的监听;而 watch 首次渲染时,不进行数据的监听,需要设置 immediate: true 后,开始进行数据的监听。

四、v-for 没有 key 会发生什么问题?

  • 低效的渲染:Vue 使用 key 来跟踪每个节点的身份,以便在数据发生变化时高效地更新 DOM。如果没有 keyVue 将无法区分相邻的元素,从而导致不必要的重新渲染和性能下降。
  • 错误的组件状态:在某些情况下,没有 key 可能会导致组件状态错误。例如,如果你在 v-for 中使用了组件,并且该组件依赖于特定的状态,而没有使用 key 进行区分,可能会导致状态错误或不一致。
  • 不可预测的行为:没有 key 可能会使 Vue 的行为变得不可预测。例如,如果在使用 v-for 进行列表渲染时,你尝试对列表进行排序或过滤,而没有使用 key 来标识每个元素,Vue 可能无法正确地更新 DOM

五、v-model 是什么?有什么用呢?

  • v-model 是用于双向绑定数据的一种指令。它可以将表单元素的值与 Vue 实例中的数据进行关联,实现数据的双向同步。
  • v-model 可以用于多种表单元素,如输入框、复选框、单选按钮、下拉列表等。它会自动根据不同的表单元素类型,处理用户的输入和更新数据,非常方便。
  • v-model 指令不仅可以用于普通的表单元素,还可以用于自定义的组件。通过在自定义组件上使用 v-model 指令,可以实现组件数据的双向绑定和更新。

六、在 createdmounted 生命周期中,发送网络数据有什么区别?

  • created 钩子函数在实例创建之后,数据观测(data observer)初始化之后,同步数据更新和脏检查之前被调用。因此,在这个阶段,你可以做一些初始化的工作,如设置默认值、计算属性等,但不可以访问到 DOM 元素。
  • mounted 钩子函数在实例被挂载到 DOM 上之后调用。在这个阶段,你可以访问到所有的 DOM 元素和子组件,可以进行一些 DOM 操作或调用一些需要 DOM 元素的操作。
  • 关于发送网络请求,一般来说,应该在 mounted 钩子函数中进行。因为在这个阶段,你已经可以访问到 DOM 元素,可以获取到输入框中的值、按钮的状态等信息,然后可以将这些信息发送到服务器。如果过早地在 created 中发送请求,可能获取到的数据并不准确,例如,一些需要根据用户输入或其他状态变化的值可能还没有初始化。
  • 但请注意,这并不是绝对的。如果你的请求只是获取一些基础数据,而这些数据并不依赖于 DOM 元素的状态,那么在 created 中发送请求也是可以的。总的来说,你需要根据具体的需求和情况来确定在哪个钩子函数中发送网络请求。

七、Vue3 有哪些新的组件?

  • Fragment:在 Vue2 中要求必须有一个根组件,但在 Vue3 中,可以没有根标签,内部会将多个标签包含在一个 Fragment 虚拟元素中,以减少标签层级,减小内存占用。
  • Teleport:可以将一个组件内部的一部分模板传送到该组件的 DOM 结构外层的位置去。
  • Suspense:用于在组件树中协调对异步依赖的处理。它让我们可以在组件树上层等待下层的多个嵌套异步依赖项解析完成,并可以在等待时渲染一个加载状态。

八、组件之间的通信方式有哪些?

  • Props:这是最常见的一种通信方式。父组件可以通过 props 向子组件传递数据,子组件通过定义 props 来接收这些数据。需要注意的是,子组件不应修改父组件传递过来的数据,如果需要修改,应该通过触发一个事件告诉父组件进行修改。
  • Emit:这是子组件通过触发一个事件来通知父组件的一种通信方式。在子组件中,可以通过 emit 方法触发一个事件,并传递数据给父组件。父组件可以通过侦听这个事件来获取数据。
  • RefsrefVue 提供的一种特殊属性,可以用来访问 DOM 元素或者子组件实例。ref 的值是在渲染过程中确定的,可以用作事件监听器。但需要注意的是,ref 不应在模板中直接使用,而应该通过 .value 进行访问。
  • Slots:当需要在子组件中插入父组件的内容时,可以使用 slots 通信方式。slots 允许父组件的内容渲染到子组件的模板中。
  • Provide / Inject:这是一种祖先级组件向后代组件传递数据的方式。Provide 选项提供了一个全局 API,使得后代组件能够访问到它。Inject 选项使得后代组件能够获取到它。这种方式并不常用,因为它使得组件间的依赖关系变得不清晰。
  • Vuex / Pinia:这是 Vue 的专门的状态管理库。它们使得组件间的状态管理更加清晰和可控。在一个大型的应用中,通常会使用 VuexPinia 来进行全局的状态管理。

九、v-ifv-show 有什么区别?

  • 实现方式:v-if 是动态的向 DOM 元素中添加或删除元素,而 v-show 是通过设置元素的 display 样式(block 或者 none)来实现。
  • 编译过程:v-if 的切换有一个局部编译或卸载的过程,而 v-show 只是简单的切换 CSS。这意味着当条件为假时,v-if 会完全销毁和重建内部的事件监听和子组件,而 v-show 则不会。
  • 编译条件:v-if 是惰性的,如果初始的判断条件为假,则不进行编译,反之,则进行编译,而 v-show 无论初始的判断条件是否为假,都进行编译。
  • 性能消耗:v-if 有更高的切换消耗,因为它涉及到元素的销毁和重建。而 v-show 的切换开销较小,因为它只是改变 CSS 属性。

十、KeepAlive

实现原理

  • KeepAliveVue 的内置组件,其实现原理主要是在组件切换过程中,将切换出去的组件保留在缓存中,而不是销毁它们。这样,当再次需要渲染这些组件时,可以直接从缓存中取出,避免了重复渲染 DOM,从而减少了加载时间及性能消耗,提高了用户体验性。

常用属性

  • KeepAlive 具有 includeexclude 属性,通过它们可以控制哪些组件进入缓存。另外它还提供了 max 属性,通过它可以设置最大缓存数,当缓存的实例超过最大缓存数时,Vue 会移除最久没有使用的组件缓存。

生命周期

  • KeepAlive 的影响,其内部所有嵌套的组件都具有两个生命周期钩子函数,分别是 onActivated()onDeactivated()onActivated() 的时机为首次挂载以及每次从缓存中被重新插入时调用,onDeactivated() 的时机为从 DOM 上移除、进入缓存以及组件卸载时调用。

十一、v-ifv-for 哪个优先级高?

  • Vue2 中,v-for 的优先级高于 v-if;在 Vue3 中,v-if 的优先级高于 v-for