2022年Vue面试题

本次让我们系统的熟悉vue的面试常用题目,无论是给准备面试还是准备跳槽的小伙伴,一个学习以及温故知新的版块。这其中加入了我的理解,我尽量用白话文和易懂的语言形式进行解释,如果有地方解释的不正确或者不准确的情况,还请大家不吝赐教。

2022年大环境的不景气,让我们用知识充实自己,面对生活的一次次挑战与考验,加油!

入门级:

1:什么是mvvm?

MVVM是Model-View-ViewModel的简写,是M-V-VM三部分组成。它本质上是MVC的改进版本,MVVM就是将其中的View的状态和行为抽象化,其中ViewModel将视图 UI 和业务逻辑分开,它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。

MVVM采用双向数据绑定,view中数据变化将自动反映到viewmodel上,反之,model中数据变化也将会自动展示在页面上。把Model和View关联起来的就是ViewModel。ViewModel负责把Model的数据同步到View显示出来,还负责把View的修改同步回Model。

让我们开发人员可以专注于业务逻辑方面(ViewModel),对于数据的绑定与展示(自动更新dom),不需要过多的dom操作。可以理解为数据(medel)驱动UI(view),而UI(view)的改变也会影响数据(medel)。他们之间就是靠ViewModel来实现。

因此开发者只需要关注业务逻辑,不需要手动操作 DOM,也不需要关注数据状态的同步问题,这些都由 MVVM 统一管理。

2:为什么要用vue?

1.虚拟dom:dom操作是非常的消耗性能的,不再使用原生的dom操作节点的方式,模拟一个虚拟dom树,运用deff算法,只更新修改的dom节点,极大的释放了dom操作。

2.视图,数据,结构的分离:使数据的更改更为简单,不需要进行逻辑代码的修改,只需要操作数据就能完成相关操作(mvvm)

3.组件化:把一个单页应用中的各个模块拆分到一个个单独的组件中,各个组件有自己的行为逻辑以及自己的样式,相互不干扰。便于开发,以及后期维护。相同组件的公用也大大节约了开发成本。

3:vue的生命周期(11个钩子函数)

1.beforeCreate(创建前):在此生命周期函数执行的时候,data和methods中的数据还没有初始化。

2.created(创建后):在这个生命周期中,data 和 methods 都已经被初始化好了,可以访问到data中的数据以及methods的方法。

3.beforeMount(载入前):在此生命周期函数执行的时候,模板已经在内存中编译好了,但是尚未挂载到页面中去,此时页面还是旧的。

4.mounted(载入后):此时页面和内存中是最新数据,dom树创建,此时才可以操作dom节点。

5.beforeUpdate(更新前):顾名思义,这个时候页面正准备进行更新,只是还未开始更新动作,所以页面中的数据还是旧的,但是data中的数据已经是新的,只是页面并未和最新数据同步。

6.Updated(更新后):此时页面显示数据和最新的 data 数据同步。

7.beforeDestroy(销毁前):组件销毁前的钩子函数,此时实例上的data,methods,以及过滤器等都处于可用状态。我们一般可以在这个钩子内清除当前组件的定时器(clearInterval() )

8.destroyed(销毁后):此时组件已经被完全销毁,实例中的所有的数据、方法、属性、过滤器…等都已经不可用了

// 以上8个生命周期钩子函数是我们常用的钩子函数,接下来的3个钩子函数可以在特定的时候使用,方便我们理解

9.activated(组件激活时):在使用被keep-alive缓存的组件时,进入函数。如果一个子页面或者一个组件被keep-alive包含,那么当这个页面或组件第一次时会走一次完整的生命周期,当此组件无法被销毁。等重复使用的时候,会从缓存里面直接调出,此时组件不在执行前4个生命周期钩子(因为它并没有被销毁,不需要重新创建)。取而代之的是进入activated钩子函数。表示该页面或组件被激活。

10.deactivated(组件未激活时):实例没有被激活时(同9)。

11.errorCaptured(错误调用):当捕获一个来自后代组件的错误时被调用

注意:有很多面试会问父子组件创建销毁的生命周期顺序:
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted

即父组件走到beforeMount 后,开始走完子组件的完整生命周期,然后再走父组件mounted

这样理解:
1.有父才有子,所以一开始是父组件先创建实例。
2.mounted本身就是挂在到页面上的钩子,所以必须要等所有的子组件完成后再一起挂载到页面中。(既然是父,当然要包含所有的子)

4:watch、computed 和 methods 的区别

  • methods 方法:组件内部的业务逻辑方法的集合
  • computed 计算属性:依赖已有的变量来计算一个目标变量,例如根据data的变量来计算新的变量,用来格式化。或者引用store,...mapState的语法糖引用state。(computed是有缓存的,注意不能进行异步操作)
  • watch 监听:监听某一个变量的变化,并且执行相应的回调函数(watch没有缓存,目标变化一次执行一次,可以进行异步操作)

许多面试官喜欢问computed 和watch的区别,这里请多加理解

5.Vue.js的特点

  • 简洁:页面由HTML模板+Json数据+Vue实例组成,结构简单易懂
  • 数据驱动:自动计算属性和追踪依赖的模板表达式
  • 组件化:用组件来构造页面,可复用,维护方便
  • 轻量:代码量小,不依赖其他库
  • 快速:精确有效批量 DOM 更新
  • 模板友好:可通过 npm,bower 等多种方式安装,很容易融入

6:插槽的理解

插槽用于决定将所携带的内容,插入到子组件指定的某个位置,但内容必须在父组件中子组件的标签内定义,在子组件中用标签接收。slot 是组件内部的占位符。即组件编写时留给组件使用者的一个插入口。使用者可以在此编写自己的html格式以及样式,插入的位置由编写者制定

7.Vue组件之间传参

1.父传子:

  • 父组件通过props方式传递数据
  • 父组件也可以直接选择子节点的形式选中子组件的实例
this.$refs.nodeName // refs是根据组件名字来取的响应组件的属性
this.$children[2] // 返回一个数组(使用几率小,一般不这样调用)

2.子传父:

  • 子组件用$emit方法,触发父组件的监听
  • this.$parnet获取当前组件的父组件实例

3.兄弟组件之间(这里写的是兄弟组件之间,实际上是所有组件之间都可以,包括父子)

  • 用一个事件总线,evenBus。新建一个空的vue实例作事件总线(为了方便调用一般绑定到vue原型),然后用$on监听事件,用$emit触发事件。
this.$eventBus.$on('sayName',(data)=>{
  console.log(data)
}) // 监听事件总线中sayName方法
this.$eventBus.$emit('sayName',data)

详细讲解请移步到:https://www.jianshu.com/p/267e17c59d32

  • Vuex:Vuex 是一个专门为 Vue.js 应用程序开发的状态管理模式。在下面的复习中我们会详细讲解到

4.浏览器本地缓存,例如localStorage,sessionStorage

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

data 是一个函数时,每个组件实例都有自己的作用域,每个实例相互独立,不会相互影响。Object 是引用数据类型,如果不用 function 返回,每个组件的 data 都是内存的同一个地址(储存的是地址,指针,此处可以复习浅拷贝和深拷贝),一个数据改变了其他也改变了。
理解就是运用了function的函数作用域让他变成局部作用域,这样每复用一次组件,就会返回一份新的 data,类似于给每个组件实例创建一个私有的数据空间

9.路由懒加载(首页加载优化)

在单页应用中,如果没有应用懒加载,运用 webpack 打包后的文件将会异常的大,造成进入首页时,需要加载的内容过多,延时过长,不利于用户体验,而运用懒加载则可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力,减少首页加载用时

原理:vue 异步组件技术:异步加载,vue-router 配置路由 , 使用 vue 的异步组件技术 , 实现按需加载


import Vue from 'vue'
import Router from 'vue-router'
// import HelloWorld from '@/components/HelloWorld'
Vue.use(Router)
export default new Router({
  routes: [
//     {
//       path: '/',
//       name: 'HelloWorld',
//       component: HelloWorld
//     }
        {
          path: '/',
          name: 'HelloWorld',
          component: () => import('@/components/HelloWorld.vue')
        }
  ]
})

10:请说出 vue.cli 项目中 src 目录每个文件夹和文件的用法`

assets 文件夹是放静态资源;
components 是放组件;
router 是定义路由相关的配置;
store vuex模块;
view 视图;
app.vue 是一个应用主组件;
main.js 是入口文件

11.Vue 中 key 值的作用

当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。key 的作用主要是为了高效的更新虚拟 DOM。
key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速

12:vue 的指令

  • v-bind:给元素绑定属性,简写为:

  • v-on:给元素绑定事件,简写为@

  • v-html:给元素绑定数据,且该指令可以解析 html 标签

  • v-text:给元素绑定数据,不解析标签

  • v-model:数据双向绑定

  • v-for:遍历数组

  • v-show:条件渲染指令,将不符合条件的数据隐藏(display:none)

  • v-if:条件渲染指令,动态在 DOM 内添加或删除 DOM 元素(无节点)

  • v-else:条件渲染指令,必须跟 v-if 成对使用

  • v-else-if:判断多层条件,必须跟 v-if 成对使用

  • v-cloak:解决插值闪烁问题

  • v-once:只渲染元素或组件一次

  • v-pre:跳过这个元素以及子元素的编译过程,以此来加快整个项目的编译速度

13.v-for 与 v-if

因为v-for的优先级高于v-if,势必会先遍历数组,然后再判断是否显示,这样就会影响速度,尤其是只需要渲染很小一部分的时候,渲染了许多无用的节点,增加许多无用的dom操作,建议用computed先把需要的数组格式化出来

{{item}}
computed() { list() { return [1, 2, 3, 4, 5, 6, 7].filter(item => item !== 3) } }

14.Vue怎么兼容IE

使用 babel-polyfill 插件省省吧,2022年6月16日起ie都退役了

15.Vue 怎么重置 data

在很多情况下我们会遇到初始化data的时候,比如在调用element的dialog组件时(组件是v-show控制,会保留之前的状态)

Object.assign(this.$data, this.$options.data()) // 初始化data

16.route 和 router

  • route 是“路由信息对象”,包括 path,params,hash,query,fullPath,matched,name 等路由信息参数。
    route查询修改当前路由的对象
  • router 是“路由实例对象”,包括了路由的跳转方法(push、go),钩子函数等。
    router获取当前路由实例,调用方法

17.Vue的修饰符有哪些?

常用修饰符:

  • .stop 阻止事件继续传播
  • .prevent 阻止标签默认行为
  • .capture 使用事件捕获模式,即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理
  • .self 只当在 event.target 是当前元素自身时触发处理函数
  • .once 事件将只会触发一次
  • .passive 告诉浏览器你不想阻止事件的默认行为
  • .right 使用鼠标右键触发事件@click.right

中级:

1.虚拟 DOM 原理

虚拟 DOM,其实就是用对象的方式取代真实的DOM操作,把真实的DOM操作放在内存当中,在内存中的对象里做模拟操作。当页面打开时浏览器会解析 HTML 元素,构建一颗 DOM树,将状态全部保存起来,在内存当中模拟我们真实的 DOM操作,操作完后又会生成一颗 dom 树,两颗DOM树进行比较,根据 diff 算法比较两颗 DOM树不同的地方,只渲染一次不同的地方。
用diff算法把虚拟DOM和真实DOM比较,找出不同的地方,页面只渲染不同的地方

2:nextTick 的理解

Vue 是异步修改 DOM 的,并且不鼓励开发者直接接触 DOM,但是有时候需要必须对数据更改后的 DOM 元素做相应的处理,但是获取到的 DOM 数据并不是更改后的数据,这时候就需要 this.$nextTick();
很常见一个例子,在数据变化后马上要使用ref进行节点操作,由于vue是异步修改dom的,所以在数据变化后可能没有找到需要操作的dom,故而报错

methods() { this.isShow = true; this.$nextTick(()=>{ this.$refs.mydom.play() // 等待最新的dom更新了之后的回调,此时已经有了mydom节点,可以对其进行dom操作 }) },

3.不需要响应式的数据应该怎么处理?

在我们的Vue开发中,会有一些数据,从始至终都未曾改变过,这种死数据,既然不改变,那也就不需要对他做响应式处理了,不然只会做一些无用功消耗性能,比如一些写死的下拉框,写死的表格数据,这些数据量大的死数据,如果都进行响应式处理,那会消耗大量性能。

// 方法一:将数据定义在return 之外(初始化时期只会对data内的数据绑定getter和setter)
data () {
    this.list1 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
    this.list2 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
    this.list3 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
    this.list4 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
    this.list5 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
    return {}
 }
    
// 方法二:Object.freeze()
data () {
    return {
        list1: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
        list2: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
        list3: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
        list4: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
        list5: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
    }
 }

4.vue 中的双向绑定原理

概念:双向绑定是vue的一个核心功能,所谓双向绑定就是当视图发生改变的时候传递给VM(ViewModel ),让数据得到更新,当数据发生改变的时候传给VM(ViewModel ),使得视图发生变化。

那么vue怎么做到的呢?
observer(观察者),劫持监听所有属性,什么意思呢?

vue.js是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的settergetter,在数据变动时发布消息给订阅者,触发相应的监听回调来渲染视图。

先简单的实现一个js的双向数据绑定来熟悉一下Object.defineProperty()方法

 
    输入的值为:
    

共有 0 条评论

沙发空余