2024前端面试系列-- Vue 篇

Vue 2中的生命周期Vue 3中的生命周期描述
beforeCreatebeforeCreate创建前,此时data和 methods的数据都还没有初始化
createdcreated创建后,data中有值,尚未挂载,可以进行一些Ajax请求
beforeMountbeforeMount挂载前,会找到虚拟DOM,编译成Render
mountedmounted挂载后,DOM已创建,可用于获取访问数据和DOM元素
beforeUpdatebeforeUpdate更新前,可用于获取更新前各种状态
updatedupdated更新后,所有状态已是最新
beforeDestroybeforeUnmount销毁前,可用于一些定时器或订阅的取消
destroyedunmounted销毁后,可用于一些定时器或订阅的取消
activatedactivatedkeep-alive缓存的组件激活时
deactivateddeactivatedkeep-alive缓存的组件停用时
errorCapturederrorCaptured捕获一个来自子孙组件的错误时调用
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()

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

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

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

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

建立更新机制这一步会立即执行一次组件的更新函数,这会首次执行组件渲染函数并执行patch将vnode 转换为 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对象,只不过是通过不同的属性去描述一个视图结构。

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

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

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

  2. 对比方式:
    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,子向父是通过 e m i t 触发事件;通过父链 / 子链也可以通信( emit触发事件;通过父链/子链也可以通信( emit触发事件;通过父链/子链也可以通信(parent/ c h i l d r e n ); r e f 也可以访问组件实例; p r o v i d e / i n j e c t ; children);ref也可以访问组件实例;provide/inject; children);ref也可以访问组件实例;provide/injectattrs/$listeners。

  1. 兄弟组件通信:

全局事件总线EventBus、Vuex。

  1. 跨层级组件通信:

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

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

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

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

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

  5. 性能消耗不同。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缓存的组件,会多出两个生命周期钩子:activated、deactivated。

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

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

mixin

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

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

劣势:

  1. 变量来源不明确

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

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

插槽

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

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

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

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

  1. scope=“接收的变量名”:

  2. slot-scope=“接收的变量名”:

  3. v-slot:插槽名=“接收的变量名”:<template v-slot:插槽名=“接收的变量名”>

大厂面试题分享 面试题库

前端面试题库 (面试必备) 推荐:★★★★★

地址:前端面试题库

Vue 中的修饰符有哪些?

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

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

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

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

  4. 键值修饰符
    键盘修饰符是用来修饰键盘事件(onkeyup,onkeydown)的,有如下: keyCode存在很多,但vue为我们提供了别名,分为以下两种:

  • 普通键(enter、tab、delete、space、esc、up…)

  • 系统修饰键(ctrl、alt、meta、shift…)

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

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

SPAMPA
组成一个主页面和多个页面片段多个主页面
url模式hash模式history模式
SEO搜索引擎优化难实现,可使用SSR方式改善容易实现
数据传递容易通过url、cookie、localStorage等传递
页面切换速度快,用户体验良好切换加载资源,速度慢,用户体验差
维护成本相对容易相对复杂
  1. SPA的优缺点:
    优点:
  • 具有桌面应用的即时性、网站的可移植性和可访问性

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

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

缺点:

  • 不利于搜索引擎的抓取

  • 首次渲染速度相对较慢

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

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

  3. 原理:

Vue 面试题

1.Vue 双向绑定原理
2.描述下 vue 从初始化页面–修改数据–刷新页面 UI 的过程?
3.你是如何理解 Vue 的响应式系统的?
4.虚拟 DOM 实现原理
5.既然 Vue 通过数据劫持可以精准探测数据变化,为什么还需要虚拟 DOM 进行 diff 检测差异?
6.Vue 中 key 值的作用?
7.Vue 的生命周期
8.Vue 组件间通信有哪些方式?
9.watch、methods 和 computed 的区别?
10.vue 中怎么重置 data?
11.组件中写 name 选项有什么作用?
12.vue-router 有哪些钩子函数?
13.route 和 router 的区别是什么?
14.说一下 Vue 和 React 的认识,做一个简单的对比
15.Vue 的 nextTick 的原理是什么?
16.Vuex 有哪几种属性?
17.vue 首屏加载优化
18.Vue 3.0 有没有过了解?
19.vue-cli 替我们做了哪些工作?
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

I 的过程?
3.你是如何理解 Vue 的响应式系统的?
4.虚拟 DOM 实现原理
5.既然 Vue 通过数据劫持可以精准探测数据变化,为什么还需要虚拟 DOM 进行 diff 检测差异?
6.Vue 中 key 值的作用?
7.Vue 的生命周期
8.Vue 组件间通信有哪些方式?
9.watch、methods 和 computed 的区别?
10.vue 中怎么重置 data?
11.组件中写 name 选项有什么作用?
12.vue-router 有哪些钩子函数?
13.route 和 router 的区别是什么?
14.说一下 Vue 和 React 的认识,做一个简单的对比
15.Vue 的 nextTick 的原理是什么?
16.Vuex 有哪几种属性?
17.vue 首屏加载优化
18.Vue 3.0 有没有过了解?
19.vue-cli 替我们做了哪些工作?
[外链图片转存中…(img-mtrp7GrU-1718450790475)]

  • 17
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值