Skip to main content

vue面试题

谈谈你对 vue 的理解 ?

Vue 是一套用于构建用户界面的渐进式框架,Vue 的核心库只关注视图层

  • 声明式框架
  • MVVM 模式响应式数据绑定
  • 虚拟 DOM(它能够在内存中构建虚拟 DOM 树,通过比较虚拟 DOM 的差异来最小化实际 DOM 的操作,提升性能。)
  • 指令和插件系统

Vue 为什么需要虚拟 DOM?

  • 真实 DOM 的抽象 , 减少了对真实 DOM 的操作
  • 跨平台
  • 如何生成的 template-》 render-》 返回虚拟 dom | h-》 createVNode

谈一谈对 Vue 组件化的理解?

  • 高内聚、可重用、可组合
  • 降低更新范围,只重新渲染变化的组件;
  • 每个组件都有一个渲染函数 effect 数据变化就就会执行 合理拆分

你知道哪些 Vue3 新特性?

  • Composition API
  • setup 语法
  • Teleport 传送门
  • Fragments
  • Emits Component Option (vue2 需要用.native, vue3 直接绑定到了根元素上)
  • createRenderer 提供自定义渲染器
  • v-bind in <style>
  • 在作用域样式中可以包含全局规则或只针对插槽内容的规则 :deep(h1) :global(.root) :slotted(.child)
  • Suspense

Vue3 对⽐ Vue2 的变化

  • 性能优化(更快)

    • Proxy defineProperty 递归 $set $delets
    • 模板编译优化
    • diff PatchFlag
  • 体积优化(更小)

    • Vue3 移除了不常用的 API

      • $on $once .sync 过滤器 全局 API
    • Tree-shaking 机制实现按需引入

  • 支持自定义渲染器 跨平台 以前需要改源码

  • TypeScript 支持

  • monorepo

Composition API 和 Options API

  • 反复横跳
  • 无 this 问题
  • Tree-shaking 支持友好
  • 组件复用 Vue2 采用 mixins

请说一下你对响应式数据的理解?

  • Vue2 defineProperty 数据劫持 重写 getter 和 setter 得递归 删除新增需要通过$set和$delete 不支持 Map 和 Set
  • Vue3 采用 Proxy

双向绑定的理解,以及它的实现原理吗?

  • 它用于将模型数据和视图数据进行同步,使得当模型数据发生变化时,视图会自动更新;当视图数据发生变化时,模型数据也会自动更新。双向绑定在表单处理、实时数据显示等方面提供了极大的便利性。

  • 响应式系统(Reactivity System): Vue 3 使用 Proxy 代替了 Vue 2 中的 Object.defineProperty,从而劫持对象的读写操作。Proxy 更强大,可以劫持对对象的动态添加和删除属性的操作。

  • 依赖收集(Dependency Tracking): Vue 3 使用 effect 函数来收集依赖。当一个响应式对象的属性被访问时,当前活跃的 effect 函数会被记录为该属性的依赖者。当该属性发生变化时,这些依赖者会被重新执行,从而更新视图。

  • 模板编译(Template Compilation): Vue 3 中,模板被编译成渲染函数。渲染函数在执行过程中会触发响应式数据的读取操作,从而完成依赖收集。当响应式数据变化时,渲染函数会重新执行,更新视图。

  • 指令(Directives): Vue 3 中使用 v-model 指令实现双向绑定。v-model 本质上是对 input 和 change 事件和绑定属性(如 value)的封装,用来实现数据和视图的双向同步。

生命周期对比

生命周期 v2生命周期 v3描述
beforeCreatebeforeCreate组件实例被创建之初
createdcreated组件实例已经完全创建
beforeMountbeforeMount组件挂载之前
mountedmounted组件挂载到实例上去之后
beforeUpdatebeforeUpdate组件数据发生变化,更新之前
updatedupdated数据数据更新之后
beforeDestroybeforeUnmount组件实例销毁之前
destroyedunmounted组件实例销毁之后
activatedactivatedkeep-alive 缓存的组件激活时
deactivateddeactivatedkeep-alive 缓存的组件停用时调用
errorCapturederrorCaptured捕获一个来自子孙组件的错误时被调用
-renderTracked Dev调试钩子,响应式依赖被收集时调用
-renderTriggered Dev调试钩子,响应式依赖被触发时调用
-serverPrefetchssr only,组件实例在服务器上被渲染前调用

Vue3 中新增了,组合式 API:生命周期钩子,但是不存在 onBeforeCreate 和 onCreated 钩子

如何理解 reactive、ref 、toRef 和 toRefs?

  • reactive::将一个普通对象转换为响应式对象。(采用 new Proxy 进行实现) 通过代理对象访问属性时会进行依赖收集,属性更新时会触发依赖更新。
  • ref: 创建一个包装对象(Wrapper Object)将一个简单的值包装成一个响应式对象,当访问value属性时会进行依赖收集,更新value属性时会触发依赖更新。(采用类访问器实现) 内部是对象的情况会采用 reactive 来进行处理
  • toRef::创建ref对象,引用reactive中的属性。
  • toRefs::批量创建ref对象,引用reactive中的属性。

watch 和 watchEffect 的区别?

  • watchEffect 立即运行一个函数,然后被动地追踪它的依赖,当这些依赖改变时重新执行该函数。
  • watch 侦测一个或多个响应式数据源并在数据源变化时调用一个回调函数。
  • 底层都是基于 effect 函数实现 接受一个 getter 函数和 scheduler 依赖收集和触发更新

computed 和 watch 区别?

底层都是基于 effect 实现

用途

  • computed 用于声明计算属性,主要用于基于数据计算出新的值。
  • watch 用于监听数据变化,适合执行副作用操作。

性能

  • computed 会缓存计算结果,只有在依赖的数据发生变化时才会重新计算。
  • watch 每次数据变化都会触发回调函数,执行逻辑可能较为昂贵。

使用场景

  • 使用 computed 当你需要基于响应式数据计算某个值时。
  • 使用 watch 当你需要在数据变化时执行异步操作或副作用时。

响应式依赖

  • computed 自动追踪依赖,只有在依赖变化时重新计算。
  • watch 手动指定要监听的响应式数据。

Vue 中如何进行依赖收集

  • Vue3 中会通过 Map 结构将属性和 effect 映射起来。
  • 默认在初始化时会调用 render 函数,此时会触发属性依赖收集 track,
  • 当属性发生修改时会找到对应的 effect 列表依次执行 trigger

Vue 中如何检测数组变化?

Vue2 中重写数组方法 Vue3 Proxy 实现了更高效和精确的数组变化检测 但是由于代理问题 也对部分数组进行重写

Vue 中 diff 算法原理

vue 基于虚拟 DOM 做更新 。diff 的核心就是比较两个虚拟节点的差异 。Vue 的 diff 算法是平级比较,不考虑跨级比较的情况。内部采用深度递归的方式 + 双指针的方式进行比较。

  • 比较 key 和 type 是否能复用
  • 看新老节点的类型
  • 都是数组 然后核心比较
  • 头头
  • 尾尾
  • 特殊情况的挂载
  • 特殊情况的卸载
  • 没比较完 最长递归子序列 移动老节点最少情况

谈谈 Vue3 中模板编译做了哪些优化?

  • PatchFlags 优化 靶向更新
  • BlockTree
  • 静态提升 每次调用 render 函数都要重新创建虚拟节点。 静态提升则是将静态的节点或者属性提升出去。静态提升是以树为单位。也就是说树中节点有动态的不会进行提升。
  • 预字符串化 静态提升的节点都是静态的,我们可以将提升出来的节点字符串化。 当连续静态节点超过 20 个时,会将静态节点序列化为字符串。
  • 缓存函数 每次调用 render 的时都要创建新函数 开启函数缓存后,函数会被缓存起来,后续可以直接使用

如何将 template 转换成 render 函数 ?

  1. 将 template 模板转换成 ast 语法树 - parserHTML
  2. Vue2 对静态语法做静态标记 - optimize / Vue3 对 ast 语法进行转化 - transform
  3. 重新生成代码 - codeGen

v-if & v-show

  • v-if 可以阻断内部代码是否执行

v-if & v-for 优先级

  • 在 Vue2 中解析时,先解析 v-for 在解析 v-if。会导致先循环后在对每一项进行判断,浪费性能。
  • 在 Vue3 中 v-if 的优先级高于 v-for。
  • 计算属性在 v-for 循环外部过滤数组

Vue.use 是干什么的?

  • 安装 Vue.js 插件。如果插件是一个对象,必须提供 install 方法。如果插件是一个函数,它会被作为 install 方法。install 方法调用时,会将 Vue 作为参数传入,这样插件中就不在需要依赖 Vue 了。(扩展应用的功能)
  • 添加全局指令、全局过滤器(Vue3 不再支持过滤器)、全局组件。
Vue.use = function (plugin: Function | Object) {
// 插件缓存
const installedPlugins = this._installedPlugins || (this._installedPlugins = []);
if (installedPlugins.indexOf(plugin) > -1) {
// 如果已经有插件 直接返回
return this;
}
// additional parameters
const args = toArray(arguments, 1); // 除了第一项其他的参数整合成数组
args.unshift(this); // 将Vue 放入到数组中
if (typeof plugin.install === "function") {
// 调用install方法
plugin.install.apply(plugin, args);
} else if (typeof plugin === "function") {
// 直接调用方法
plugin.apply(null, args);
}
installedPlugins.push(plugin); // 缓存插件
return this;
};

说说你对 nextTick 的理解?

  • 当你在 Vue 中更改响应式状态时,最终的 DOM 更新并不是同步生效的,而是由 Vue 将它们缓存在一个队列中,直到下一个“tick”才一起执行。这样是为了确保每个组件无论发生多少状态改变,都仅执行一次更新。
const resolvedPromise = Promise.resolve();
let currentFlushPromise;
export function nextTick(fn) {
const p = currentFlushPromise || resolvedPromise;
return fn ? p.then(this ? fn.bind(this) : fn) : p;
}

vue2

  • [渲染 watcher, nextTick 逻辑,数据更新] 页面要等同步代码执行完才执行下一个 tick
  • 比如说我先写 nextTick 然后修改数据更新页面 我在之前的 nextTick 中是会获取不到最新的数据的 因为先执行的 nextTick 然后修改的页面 优雅降级(Promise、MutationObserver、setImmediate、setTimeout) 这里一般会配合浏览器事件环作为面试题。

vue3

  • 当执行 mounted 之前会创建一个 promise,nextTick 会被延迟到这个 promise 之后执行 (值被改为 100 后再进行渲染)
  • 比如说我先写 nextTick 然后修改数据更新页面 我在之前的 nextTick 此时 DOM 已经更新 是可以获取到数据的

Vue 中如何进行组件通信?

props 父子间通信 $attrs 父子间通信 $emit 子父通信 expose / ref / $parent 实例通信 v-model 数据同步 provide / inject 跨级通信 slot ui 通信 Vuex/pinia 状态管理 mitt 发布订阅通信(事件总线)

Vue-Router 有几种钩子函数,具体是什么及执行流程是怎样的?

  1. beforeRouteLeave
  2. beforeEach
  3. BeforeEnter
  4. BeforeRouteEnter
  5. BeforeResolve 导航确认
  6. afterEach‘ 页面更新
  7. BeforeRouteEnter 里面的 next 函数 参数为组件的实例

导航被触发。 在失活的组件里调用 beforeRouteLeave 守卫。 调用全局的 beforeEach 守卫。 在重用的组件里调用 beforeRouteUpdate 守卫(2.2+)。 在路由配置里调用 beforeEnter。 解析异步路由组件。 在被激活的组件里调用 beforeRouteEnter。 调用全局的 beforeResolve 守卫(2.5+)。 导航被确认。 调用全局的 afterEach 钩子。 触发 DOM 更新。 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。