2023前端面试系列-- Vue 篇

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

大厂面试题分享 面试题库

前端面试题库 面试必备 推荐★★★★★

地址前端面试题库

Vue 常见面试题总结


MVVM模型

MVVM是Model-View-ViewModel的简写其本质是MVC模型的升级版。其中 Model 代表数据模型View 代表看到的页面ViewModelViewModel之间的桥梁数据会绑定到ViewModel层并自动将数据渲染到页面中视图变化的时候会通知ViewModel层更新数据。以前是通过操作DOM来更新视图现在是数据驱动视图

Vue的生命周期

每个 Vue 组件实例在创建后都会经过一系列的初始化过程这个过程中会运行叫做生命周期钩子的函数以便于用户在特定的阶段有机会添加自己的代码。

Vue 的生命周期可以分为8个阶段创建前后、挂载前后、更新前后、销毁前后以及一些特殊场景的生命周期。Vue 3 中还新增了是3个用于调试和服务端渲染的场景。

Vue 2中的生命周期

Vue 3中的生命周期

描述

beforeCreate

beforeCreate

创建前此时datamethods的数据都还没有初始化

created

created

创建后data中有值尚未挂载可以进行一些Ajax请求

beforeMount

beforeMount

挂载前会找到虚拟DOM编译成Render

mounted

mounted

挂载后DOM已创建可用于获取访问数据和DOM元素

beforeUpdate

beforeUpdate

更新前可用于获取更新前各种状态

updated

updated

更新后所有状态已是最新

beforeDestroy

beforeUnmount

销毁前可用于一些定时器或订阅的取消

destroyed

unmounted

销毁后可用于一些定时器或订阅的取消

activated

activated

keep-alive缓存的组件激活时

deactivated

deactivated

keep-alive缓存的组件停用时

errorCaptured

errorCaptured

捕获一个来自子孙组件的错误时调用

renderTracked

调试钩子响应式依赖被收集时调用

renderTriggered

调试钩子响应式依赖被触发时调用

serverPrefetch

组件实例在服务器上被渲染前调用

父子组件的生命周期

  • 加载渲染阶段父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted

  • 更新阶段父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated

  • 销毁阶段父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed

Vue.$nextTick

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法获取更新后的 DOM。

nextTick 是 Vue 提供的一个全局 API由于 Vue 的异步更新策略导致我们对数据修改后不会直接体现在 DOM 上此时如果想要立即获取更新后的 DOM 状态就需要借助该方法。

Vue 在更新 DOM 时是异步执行的。当数据发生变化Vue 将开启一个异步更新队列并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发只会被推入队列一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。nextTick方法会在队列中加入一个回调函数确保该函数在前面的 DOM 操作完成后才调用。

使用场景

  1. 如果想要在修改数据后立刻得到更新后的DOM结构可以使用Vue.nextTick()

  1. 在created生命周期中进行DOM操作

Vue 实例挂载过程中发生了什么

挂载过程指的是 app.mount()过程这是一个初始化过程整体上做了两件事情初始化建立更新机制

初始化会创建组件实例、初始化组件状态、创建各种响应式数据。

建立更新机制这一步会立即执行一次组件的更新函数这会首次执行组件渲染函数并执行patchvnode 转换为 dom 同时首次执行渲染函数会创建它内部响应式数据和组件更新函数之间的依赖关系这使得以后数据发生变化时会执行对应的更新函数。

Vue 的模版编译原理

Vue 中有个独特的编译器模块称为compiler它的主要作用是将用户编写的template编译为js中可执行的render函数。

在Vue 中编译器会先对template进行解析这一步称为parse结束之后得到一个JS对象称之为抽象语法树AST然后是对AST进行深加工的转换过程这一步称为transform最后将前面得到的AST生成JS代码也就是render函数。

Vue 的响应式原理

  1. Vue 2 中的数据响应式会根据数据类型做不同的处理。如果是对象则通过Object.defineProperty(obj,key,descriptor)拦截对象属性访问当数据被访问或改变时感知并作出反应如果是数组则通过覆盖数组原型的方法扩展它的7个变更方法push、pop、shift、unshift、splice、sort、reverse使这些方法可以额外的做更新通知从而做出响应。
    缺点

  • 初始化时的递归遍历会造成性能损失

  • 通知更新过程需要维护大量 dep 实例和 watcher 实例额外占用内存较多

  • 新增或删除对象属性无法拦截需要通过 Vue.set 及 delete 这样的 API 才能生效

  • 对于ES6中新产生的Map、Set这些数据结构不支持。

  1. Vue 3 中利用ES6的Proxy机制代理需要响应化的数据。可以同时支持对象和数组动态属性增、删都可以拦截新增数据结构均支持对象嵌套属性运行时递归用到时才代理也不需要维护特别多的依赖关系性能取得很大进步。

虚拟DOM

  1. 概念
    虚拟DOM顾名思义就是虚拟的DOM对象它本身就是一个JS对象只不过是通过不同的属性去描述一个视图结构。

  1. 虚拟DOM的好处
    (1) 性能提升
    直接操作DOM是有限制的一个真实元素上有很多属性如果直接对其进行操作同时会对很多额外的属性内容进行了操作这是没有必要的。如果将这些操作转移到JS对象上就会简单很多。另外操作DOM的代价是比较昂贵的频繁的操作DOM容易引起页面的重绘和回流。如果通过抽象VNode进行中间处理可以有效减少直接操作DOM次数从而减少页面的重绘和回流。
    (2) 方便跨平台实现
    同一VNode节点可以渲染成不同平台上对应的内容比如渲染在浏览器是DOM元素节点渲染在NativeiOS、Android变为对应的控件。Vue 3 中允许开发者基于VNode实现自定义渲染器renderer以便于针对不同平台进行渲染。

  1. 结构
    没有统一的标准一般包括tag、props、children三项。
    tag必选。就是标签也可以是组件或者函数。
    props非必选。就是这个标签上的属性和方法。
    children非必选。就是这个标签的内容或者子节点。如果是文本节点就是字符串如果有子节点就是数组。换句话说如果判断children是字符串的话就表示一定是文本节点这个节点肯定没有子元素。

diff 算法

  1. 概念
    diff算法是一种对比算法通过对比旧的虚拟DOM和新的虚拟DOM得出是哪个虚拟节点发生了改变找出这个虚拟节点并只更新这个虚拟节点所对应的真实节点而不用更新其他未发生改变的节点实现精准地更新真实DOM进而提高效率。

  1. 对比方式
    diff算法的整体策略是深度优先同层比较。比较只会在同层级进行, 不会跨层级比较比较的过程中循环从两边向中间收拢。

  • 首先判断两个节点的tag是否相同不同则删除该节点重新创建节点进行替换。

  • tag相同时先替换属性然后对比子元素分为以下几种情况

  • 新旧节点都有子元素时采用双指针方式进行对比。新旧头尾指针进行比较循环向中间靠拢根据情况调用patchVnode进行patch重复流程、调用createElem创建一个新节点从哈希表寻找 key一致的VNode节点再分情况操作。

  • 新节点有子元素旧节点没有子元素则将子元素虚拟节点转化成真实节点插入即可。

  • 新节点没有子元素旧节点有子元素则清空子元素并设置为新节点的文本内容。

  • 新旧节点都没有子元素时即都为文本节点则直接对比文本内容不同则更新。

Vue中key的作用

key的作用主要是为了更加高效的更新虚拟 DOM

Vue 判断两个节点是否相同时主要是判断两者的key元素类型tag。因此如果不设置key 它的值就是 undefined则可能永远认为这是两个相同的节点只能去做更新操作将造成大量的 DOM 更新操作。

为什么组件中的 data 是一个函数

在 new Vue() 中可以是函数也可以是对象因为根实例只有一个不会产生数据污染。

在组件中data 必须为函数目的是为了防止多个组件实例对象之间共用一个 data产生数据污染而采用函数的形式initData 时会将其作为工厂函数都会返回全新的 data 对象。

Vue 中组件间的通信方式

  1. 父子组件通信

父向子传递数据是通过props子向父是通过$emit触发事件通过父链/子链也可以通信$parent/$childrenref也可以访问组件实例provide/inject$attrs/$listeners。

  1. 兄弟组件通信

全局事件总线EventBus、Vuex。

  1. 跨层级组件通信

全局事件总线EventBus、Vuex、provide/inject。

v-show 和 v-if 的区别

  1. 控制手段不同。v-show是通过给元素添加 css 属性display: none但元素仍然存在而v-if控制元素显示或隐藏是将元素整个添加或删除。

  1. 编译过程不同。v-if切换有一个局部编译/卸载的过程切换过程中合适的销毁和重建内部的事件监听和子组件v-show只是简单的基于 css 切换。

  1. 编译条件不同。v-if是真正的条件渲染它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建渲染条件为假时并不做操作直到为真才渲染。

  1. 触发生命周期不同。v-show由 false 变为 true 的时候不会触发组件的生命周期v-if由 false 变为 true 的时候触发组件的beforeCreate、created、beforeMount、mounted钩子由 true 变为 false 的时候触发组件的beforeDestory、destoryed钩子。

  1. 性能消耗不同。v-if有更高的切换消耗v-show有更高的初始渲染消耗。

使用场景

如果需要非常频繁地切换则使用v-show较好如手风琴菜单tab 页签等 如果在运行时条件很少改变则使用v-if较好如用户登录之后根据权限不同来显示不同的内容。

computed 和 watch 的区别

  • computed计算属性依赖其它属性计算值内部任一依赖项的变化都会重新执行该函数计算属性有缓存多次重复使用计算属性时会从缓存中获取返回值计算属性必须要有return关键词。

  • watch侦听到某一数据的变化从而触发函数。当数据为对象类型时对象中的属性值变化时需要使用深度侦听deep属性也可在页面第一次加载时使用立即侦听immdiate属性。

运用场景

计算属性一般用在模板渲染中某个值是依赖其它响应对象甚至是计算属性而来而侦听属性适用于观测某个值的变化去完成一段复杂的业务逻辑。

v-if 和 v-for 为什么不建议放在一起使用

Vue 2 中v-for的优先级比v-if高这意味着v-if将分别重复运行于每一个v-for循环中。如果要遍历的数组很大而真正要展示的数据很少时将造成很大的性能浪费。

Vue 3 中则完全相反v-if的优先级高于v-for所以v-if执行时它调用的变量还不存在会导致异常。

通常有两种情况导致要这样做

  • 为了过滤列表中的项目比如v-for = "user in users" v-if = "user.active"。这种情况可以定义一个计算属性让其返回过滤后的列表即可。

  • 为了避免渲染本该被隐藏的列表比如v-for = "user in users" v-if = "showUsersFlag"。这种情况可以将v-if移至容器元素上或在外面包一层template即可。

$set

可手动添加响应式数据解决数据变化视图未更新问题。当在项目中直接设置数组的某一项的值或者直接设置对象的某个属性值会发现页面并没有更新。这是因为Object.defineProperty()的限制监听不到数据变化可通过this.$set(数组或对象数组下标或对象的属性名更新后的值)解决。

keep-alive 是什么

  • 作用实现组件缓存保持组件的状态避免反复渲染导致的性能问题。

  • 工作原理Vue.js 内部将 DOM 节点抽象成了一个个的 VNode 节点keep-alive组件的缓存也是基于 VNode 节点的。它将满足条件的组件在 cache 对象中缓存起来重新渲染的时候再将 VNode 节点从 cache 对象中取出并渲染。

  • 可以设置以下属性
    ① include字符串或正则只有名称匹配的组件会被缓存。
    ② exclude字符串或正则任何名称匹配的组件都不会被缓存。
    ③ max数字最多可以缓存多少组件实例。
    匹配首先检查组件的name选项如果name选项不可用则匹配它的局部注册名称父组件 components选项的键值匿名组件不能被匹配。

设置了keep-alive缓存的组件会多出两个生命周期钩子activateddeactivated

首次进入组件时beforeCreate --> created --> beforeMount --> mounted --> activated --> beforeUpdate --> updated --> deactivated

再次进入组件时activated --> beforeUpdate --> updated --> deactivated

mixin

mixin混入 它提供了一种非常灵活的方式来分发 Vue 组件中的可复用功能。

使用场景 不同组件中经常会用到一些相同或相似的代码这些代码的功能相对独立。可以通过mixin 将相同或相似的代码提出来。

劣势

  1. 变量来源不明确

  1. 多 mixin 可能会造成命名冲突解决方式Vue 3的组合API

  1. mixin 和组件坑出现多对的的关系使项目复杂度变高。

插槽

slot插槽一般在组件内部使用封装组件时在组件内部不确定该位置是以何种形式的元素展示时可以通过slot占据这个位置该位置的元素需要父组件以内容形式传递过来。slot分为

  • 默认插槽子组件用<slot>标签来确定渲染的位置标签里面可以放DOM结构作为后备内容当父组件在使用的时候可以直接在子组件的标签内写入内容该部分内容将插入子组件的<slot>标签位置。如果父组件使用的时候没有往插槽传入内容后备内容就会显示在页面。

  • 具名插槽子组件用name属性来表示插槽的名字没有指定name的插槽会有隐含的名称叫做 default。父组件中在使用时在默认插槽的基础上通过v-slot指令指定元素需要放在哪个插槽中v-slot值为子组件插槽name属性值。使用v-slot指令指定元素放在哪个插槽中必须配合<template>元素且一个<template>元素只能对应一个预留的插槽即不能多个<template> 元素都使用v-slot指令指定相同的插槽。v-slot的简写是#例如v-slot:header可以简写为#header。

  • 作用域插槽子组件在<slot>标签上绑定props数据以将子组件数据传给父组件使用。父组件获取插槽绑定 props 数据的方法

  1. scope="接收的变量名"<template scope="接收的变量名">

  1. slot-scope="接收的变量名"<template slot-scope="接收的变量名">

  1. v-slot:插槽名="接收的变量名"<template v-slot:插槽名="接收的变量名">

大厂面试题分享 面试题库

前端面试题库 面试必备 推荐★★★★★

地址前端面试题库

Vue 中的修饰符有哪些

在Vue 中修饰符处理了许多 DOM 事件的细节让我们不再需要花大量的时间去处理这些烦恼的事情而能有更多的精力专注于程序的逻辑处理。Vue中修饰符分为以下几种

  1. 表单修饰符
    lazy 填完信息光标离开标签的时候才会将值赋予给value也就是在change事件之后再进行信息同步。
    number 自动将用户输入值转化为数值类型但如果这个值无法被parseFloat解析则会返回原来的值。
    trim 自动过滤用户输入的首尾空格而中间的空格不会被过滤。

  1. 事件修饰符
    stop 阻止了事件冒泡相当于调用了event.stopPropagation方法。
    prevent 阻止了事件的默认行为相当于调用了event.preventDefault方法。
    self 只当在 event.target 是当前元素自身时触发处理函数。
    once 绑定了事件以后只能触发一次第二次就不会触发。
    capture 使用事件捕获模式即元素自身触发的事件先在此处处理然后才交由内部元素进行处理。
    passive 告诉浏览器你不想阻止事件的默认行为。
    native 让组件变成像html内置标签那样监听根元素的原生事件否则组件上使用 v-on 只会监听自定义事件。

  1. 鼠标按键修饰符
    left 左键点击。
    right 右键点击。
    middle 中键点击。

  1. 键值修饰符
    键盘修饰符是用来修饰键盘事件onkeyuponkeydown的有如下 keyCode存在很多但vue为我们提供了别名分为以下两种

  • 普通键enter、tab、delete、space、esc、up...

  • 系统修饰键ctrl、alt、meta、shift...

对 SPA 的理解

  1. 概念
    SPASingle-page application即单页面应用它是一种网络应用程序或网站的模型通过动态重写当前页面来与用户交互这种方法避免了页面之间切换时打断用户体验。在SPA中所有必要的代码HTML、JavaScript 和 CSS都通过单个页面的加载而检索或者根据需要通常是响应用户操作动态装载适当的资源并添加到页面。页面在任何时间点都不会重新加载也不会将控制转移到其他页面。举个例子就像一个杯子上午装的是牛奶中午装的是咖啡下午装的是茶变得始终是内容杯子始终不变。

  1. SPA与MPA的区别
    MPAMuti-page application即多页面应用。在MPA中每个页面都是一个主页面都是独立的每当访问一个页面时都需要重新加载 Html、CSS、JS 文件公共文件则根据需求按需加载。

SPA

MPA

组成

一个主页面和多个页面片段

多个主页面

url模式

hash模式

history模式

SEO搜索引擎优化

难实现可使用SSR方式改善

容易实现

数据传递

容易

通过url、cookie、localStorage等传递

页面切换

速度快用户体验良好

切换加载资源速度慢用户体验差

维护成本

相对容易

相对复杂

  1. SPA的优缺点
    优点

  • 具有桌面应用的即时性、网站的可移植性和可访问性

  • 用户体验好、快内容的改变不需要重新加载整个页面

  • 良好的前后端分离分工更明确

缺点

  • 不利于搜索引擎的抓取

  • 首次渲染速度相对较慢

双向绑定

  1. 概念
    Vue 中双向绑定是一个指令v-model可以绑定一个响应式数据到视图同时视图的变化能改变该值。v-model是语法糖默认情况下相当于:value和@input使用v-model可以减少大量繁琐的事件处理代码提高开发效率。

  1. 使用
    通常在表单项上使用v-model还可以在自定义组件上使用表示某个值的输入和输出控制。

  1. 原理
    v-model是一个指令双向绑定实际上是Vue 的编译器完成的通过输出包含v-model模版的组件渲染函数实际上还是value属性的绑定及input事件监听事件回调函数中会做相应变量的更新操作。

子组件是否可以直接改变父组件的数据

  1. 所有的prop都遵循着单项绑定原则props因父组件的更新而变化自然地将新状态向下流往子组件而不会逆向传递。这避免了子组件意外修改父组件的状态的情况不然应用的数据流将很容易变得混乱而难以理解。
    另外每次父组件更新后所有的子组件中的props都会被更新为最新值这就意味着不应该子组件中去修改一个prop若这么做了Vue 会在控制台上抛出警告。

  1. 实际开发过程中通常有两个场景导致要修改prop

  • prop被用于传入初始值而子组件想在之后将其作为一个局部数据属性。这种情况下最好是新定义一个局部数据属性从props获取初始值即可。

  • 需要对传入的prop值做进一步转换。最好是基于该prop值定义一个计算属性。

  1. 实践中如果确实要更改父组件属性应emit一个事件让父组件变更。当对象或数组作为props被传入时虽然子组件无法更改props绑定但仍然可以更改对象或数组内部的值。这是因为JS的对象和数组是按引用传递而对于 Vue 来说禁止这样的改动虽然可能但是有很大的性能损耗比较得不偿失。

Vue Router中的常用路由模式和原理

  1. hash 模式

  • location.hash的值就是url中 # 后面的东西。它的特点在于hash虽然出现url中但不会被包含在HTTP请求中对后端完全没有影响因此改变hash不会重新加载页面。

  • 可以为hash的改变添加监听事件window.addEventListener("hashchange", funcRef, false)每一次改变hash (window.location.hash)都会在浏览器的访问历史中增加一个记录利用hash的以上特点就可以实现前端路由更新视图但不重新请求页面的功能了。
    特点兼容性好但是不美观

  1. history 模式

利用 HTML5 History Interface 中新增的pushState()replaceState()方法。

这两个方法应用于浏览器的历史记录栈在当前已有的backforwardgo 的基础上他们提供了对历史记录进行修改的功能。

这两个方法有个共同点当调用他们修改浏览器历史记录栈后虽然当前url改变了但浏览器不会刷新页面这就为单页面应用前端路由“更新视图但不重新请求页面”提供了基础

特点虽然美观但是刷新会出现 404 需要后端进行配置。

动态路由

很多时候我们需要将给定匹配模式的路由映射到同一个组件这种情况就需要定义动态路由。例如我们有一个 User组件对于所有 ID 各不相同的用户都要使用这个组件来渲染。那么我们可以在 vue-router 的路由路径中使用动态路径参数dynamic segment来达到这个效果{path: '/user/:id', compenent: User}其中:id就是动态路径参数。

对Vuex的理解

  1. 概念
    Vuex 是 Vue 专用的状态管理库它以全局方式集中管理应用的状态并以相应的规则保证状态以一种可预测的方式发生变化。

  1. 解决的问题
    Vuex 主要解决的问题是多组件之间状态共享。利用各种通信方式虽然也能够实现状态共享但是往往需要在多个组件之间保持状态的一致性这种模式很容易出问题也会使程序逻辑变得复杂。Vuex 通过把组件的共享状态抽取出来以全局单例模式管理这样任何组件都能用一致的方式获取和修改状态响应式的数据也能够保证简洁的单向流动使代码变得更具结构化且易于维护。

  1. 什么时候用:
    Vuex 并非是必须的它能够管理状态但同时也带来更多的概念和框架。如果我们不打算开发大型单页应用或应用里没有大量全局的状态需要维护完全没有使用Vuex的必要一个简单的 store 模式就够了。反之Vuex将是自然而然的选择。

  1. 用法
    Vuex 将全局状态放入state对象中它本身是一颗状态树组件中使用store实例的state访问这些状态然后用配套的mutation方法修改这些状态并且只能用mutation修改状态在组件中调用commit方法提交mutation如果应用中有异步操作或复杂逻辑组合需要编写action执行结束如果有状态修改仍需提交mutation组件中通过dispatch派发action。最后是模块化通过modules选项组织拆分出去的各个子模块在访问状态state时需注意添加子模块的名称如果子模块有设置namespace那么提交mutation和派发action时还需要额外的命名空间前缀。

页面刷新后Vuex 状态丢失怎么解决

Vuex 只是在内存中保存状态刷新后就会丢失如果要持久化就需要保存起来。

localStorage就很合适提交mutation的时候同时存入localStoragestore中把值取出来作为state的初始值即可。

也可以使用第三方插件推荐使用vuex-persist插件它是为 Vuex 持久化储存而生的一个插件不需要你手动存取storage而是直接将状态保存至 cookie 或者 localStorage中。

关于 Vue SSR 的理解

SSR服务端渲染Server Side Render就是将 Vue 在客户端把标签渲染成 html 的工作放在服务端完成然后再把 html 直接返回给客户端。

  • 优点
    有着更好的 SEO并且首屏加载速度更快。

  • 缺点
    开发条件会受限制服务器端渲染只支持 beforeCreate 和 created 两个钩子当我们需要一些外部扩展库时需要特殊处理服务端渲染应用程序也需要处于 Node.js 的运行环境。服务器会有更大的负载需求。

了解哪些 Vue 的性能优化方法

  • 路由懒加载。有效拆分应用大小访问时才异步加载。

  • keep-alive缓存页面。避免重复创建组件实例且能保留缓存组件状态。

  • v-for遍历避免同时使用v-if。实际上在 Vue 3 中已经是一个错误用法了。

  • 长列表性能优化可采用虚拟列表。

  • v-once。不再变化的数据使用v-once。

  • 事件销毁。组件销毁后把全局变量和定时器销毁。

  • 图片懒加载。

  • 第三方插件按需引入。

  • 子组件分割。较重的状态组件适合拆分。

  • 服务端渲染。

大厂面试题分享 面试题库

前端面试题库 面试必备 推荐★★★★★

地址前端面试题库

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: vue