2022前端Vue面试题

生命周期函数面试题

1.什么是Vue生命周期?
vue生命周期是指vue是对象从创建到销毁的过程。

2.Vue生命周期的作用是什么?
在vue生命周期的不同阶段通过对应的钩子函数来实现组件数据管理和DOM渲染两大重要功能。
创建阶段:
beforecreate:实例已经初始化,但不能获取DOM节点。(没有data,没有el)
created:实例已经创建,仍然不能获取DOM节点。(有data,没有el)
载入阶段:
beforemount:模板编译完成,但还没挂载到界面上。(有data,有el)
mounted:编译好的模板已挂载到页面中(数据和DOM都已经渲染出来)。
更新阶段:
beforeupdate:数据发生变化立即调用,此时data中数据是最新的,但页面上数据仍然是旧的(检测到数据更新时,但DOM更新前执行)。
updated:更新结束后执行,此时data中的值和页面上的值都是最新的。
销毁阶段:
beforedestroy:当要销毁vue实例时,在销毁之前执行。
destroy:在销毁vue实例时执行。

3.第一次页面加载会触发哪几个钩子函数?
beforeCreate, created, beforeMount, mounted

4.简述每个生命周期具体适合哪些场景?
beforecreate:可以加Loading事件。
create:初始化完成时的事件写在这里,异步请求也适宜在这里调用(请求不宜过多,避免白屏时间太长)。
可以在这里结束loading事件,还做一些初始化,或实现函数的自执行。
此时未挂载DOM,若在此阶段进行DOM操作一定要放在Vue.nextTick()的回调函数中。
mounted:此时完成挂载DOM和渲染,需要操作DOM的方法可以放在这里,也可在这发起后端请求,拿回数据,配合路由钩子做一些事情。
beforeupdate:可在更新前访问现有的DOM,如手动移出添加的事件监听器。
updated:组件DOM已完成更新,可执行依赖的DOM操作。
注意:不要在此函数中操作数据(修改属性),会陷入死循环。
activated:在使用vue-router时有时需要使用<keep-alive></keep-alive>来缓存组件状态,这个时候created钩子就不会被重复调用了。
如果我们的子组件需要在每次加载的时候进行某些操作,可以使用activated钩子触发。
deactivated:<keep-alive></keep-alive>组件被移除时使用。
beforedestroy:销毁前,可以做一些删除提示,如:您确定删除xx吗?
destroy:销毁后,这时组件已经没有了,无法操作里面的任何东西了。

5.created和mounted的区别?
created:实例已经创建,但不能获取DOM节点。
mounted:模板已经挂载到页面上,可以操作DOM元素。

6.vue组件间通信六种方式
组件之间的传值通信
组件之间通讯分为三种: 父传子、子传父、兄弟组件之间的通讯;
(1)props/$emit

  • 父传子:props
  • 子传父:父组件向子组件传递事件方法,子组件通过$emit触发事件,回调给父组件

(2)$eimt/$on

  • 这种方法通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。
    具体实现方式:
  var Event=new Vue();
   Event.$emit(事件名,数据);
   Event.$on(事件名,data => {});
// 假设兄弟组件有三个,分别是A、B、C组件,C组件如何获取A或者B组件的数据
// 组件A、B通过Event.$emit(事件名,数据)将数据传递,组件C通过 Event.$on(事件名,data => {})接受组件A、B传递的数据

(3)Vuex

  • vuex 就是一个仓库,仓库里放了很多对象。在state中存放数据源,当组件要更改state中的数据时,必须通过mutation进行,mutation储存的是改变state中数据的操作方法,之后通过actions储存的操作去触发mutation中的方法,由组件中的$store.dispatch('action 名称', data1)来触发。然后由commit()来触发mutation的调用 , 间接更新 state。

(4)Vuex + localstorage

  • vuex 是 vue 的状态管理器,存储的数据是响应式的。但是并不会保存起来,刷新之后就回到了初始状态,具体做法应该在vuex里数据改变的时候把数据拷贝一份保存到localStorage里面,刷新之后,如果localStorage里有保存的数据,取出来再替换store里的state。
  • 这里需要注意的是:由于vuex里,我们保存的状态,都是数组,而localStorage只支持字符串,所以需要用JSON转换。

(5)$attrs/$listeners

  • $attrs:包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。
  • $listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。

(6)provide/inject

  • provide/inject:vue2.2.0 新增API,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。
  • 总而言之:祖先组件中通过provider来提供变量,然后在子孙组件中通过inject来注入变量。
  • provide / inject API主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。
// A.vue
export default {
 provide: {
   name: 'nora'
 }
}
/**************************************/
// B.vue
export default {
 inject: ['name'],
 mounted () {
   console.log(this.name);  // nora
 }
}
  • 需要注意的是:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的----vue官方文档
    provide与inject 怎么实现数据响应式?
    使用2.6最新API Vue.observable 优化响应式 provide(推荐)

  • 我们来看个例子:孙组件D、E和F获取A组件传递过来的color值,并能实现数据响应式变化,即A组件的color变化后,组件D、E、F会跟着变(核心代码如下:)

//A组件
<div>
     <h1>A 组件</h1>
     <button @click="() => changeColor()">改变color</button>
     <ChildrenB />
     <ChildrenC />
</div>
......
// provide() {
 //   return {
 //     theme: {
 //       color: this.color //这种方式绑定的数据并不是可响应的
 //     } // 即A组件的color变化后,组件D、E、F不会跟着变
 //   };
 // },

// 方法二:使用2.6最新API Vue.observable 优化响应式 provide
provide() {
   this.theme = Vue.observable({
      color: "blue"
    });
    return {
      theme: this.theme
    };
  },
  methods: {
    changeColor(color) {
      if (color) {
        this.theme.color = color;
      } else {
        this.theme.color = this.theme.color === "blue" ? "red" : "blue";
      }
    }
  }
/******************************************************************/
// F 组件 
<template functional>
 <div class="border2">
   <h3 :style="{ color: injections.theme.color }">F 组件</h3>
 </div>
</template>
<script>
export default {
 inject: {
   theme: {
     //函数式组件取值不一样
     default: () => ({})
   }
 }
};
</script>

(7)$parent / $children与 ref

  • ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
  • $parent / $children:访问父 / 子实例
  • 需要注意的是:这两种都是直接得到组件实例,使用后可以直接调用组件的方法或访问数据。
  • $parent :访问父实例,如果当前实例有的话。
  • $children:当前实例的直接子组件。需要注意 $children并不保证顺序,也不是响应式的。如果你发现自己正在尝试使用 $children来进行数据绑定,考虑使用一个数组配合 v-for 来生成子组件,并且使用 Array 作为真正的来源。
  • 这两种方法的弊端是,无法在跨级或兄弟间通信。
// component-a 子组件
export default {
 data () {
   return {
     title: 'Vue.js'
   }
 },
 methods: {
   sayHello () {
     window.alert('Hello');
   }
 }
}
/***********************************/
// 父组件
<template>
 <component-a ref="comA"></component-a>
</template>
<script>
 export default {
   mounted () {
     const comA = this.$refs.comA;
     console.log(comA.title);  // Vue.js
     comA.sayHello();  // 弹窗
   }
 }
</script>

原文参考: vue组件间通信六种方式

7.vuex是什么?怎么使用?哪种功能场景使用它?

  • vuex 就是一个仓库,仓库里放了很多对象。
  • 其中 state 存放的是数据状态,不可以直接修改里面的数据。
  • getters类似vue的计算属性,主要用来过滤一些数据。
  • mutations:存放的是动态修改Vuex的state中保存的数据状态的方法。
  • actions:保存的触发mutations中方法的方法,可以理解为通过将mutations里面处里数据的方法变成可异步的处理数据的方法,简单的说就是异步操作数据。

一般什么样的数据会放在 State 中呢?
目前主要有两种数据会使用 vuex 进行管理: 1、组件之间全局共享的数据 2、通过后端异步请求的数据 比如做加入购物车、登录状态等都可以使用Vuex来管理数据状态

  • 怎么使用Vuex? 在main.js引入store,注入。新建了一个目录store,… export
  • 场景有:单页应用中,组件之间的状态、音乐播放、登录状态、加入购物车

8.开发时,改变数组或者对象的数据,但是页面没有更新如何解决?

  • 对于数组:
    Vue 不能检测以下数组的变动:
    1.当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
    2.当你修改数组的长度时,例如:vm.items.length = newLength
//举个例子:
var vm = new Vue({
 data: {
   items: ['a', 'b', 'c']
 }
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的

//为了解决第一类问题,以下两种方式都可以实现和 vm.items[indexOfItem] = newValue 相同的效果,同时也将在响应式系统内触发状态更新:
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
// 你也可以使用 [`vm.$set`](https://cn.vuejs.org/v2/api/#vm-set) 实例方法,该方法是全局方法 `Vue.set` 的一个别名:
vm.$set(vm.items, indexOfItem, newValue)
// 为了解决第二类问题,你可以使用 `splice`:
vm.items.splice(newLength)
var vm = new Vue({
 data: {
   userProfile: {
     name: 'Anika'
   }
 }
})

//你可以添加一个新的 age 属性到嵌套的 userProfile 对象:
Vue.set(vm.userProfile, 'age', 27)
//你还可以使用 vm.$set 实例方法,它只是全局 Vue.set 的别名:
vm.$set(vm.userProfile, 'age', 27)
//有时你可能需要为已有对象赋予多个新属性,比如使用 Object.assign() 或 _.extend()。在这种情况下,你应该用两个对象的属性创建一个新的对象。
vm.userProfile = Object.assign({}, vm.userProfile, {
 age: 27,
 favoriteColor: 'Vue Green'
})

9.vue弹窗后如何禁止滚动条滚动?
参考原文:
vue弹窗屏蔽滑动的两种解决方案
vue弹窗后如何禁止滚动条滚动?
vue如何禁止弹窗后面的滚动条滚动?

10.如何在 vue 项目里正确地引用 jquery 和 jquery-ui的插件
参考原文:
如何在 vue 项目里正确地引用 jquery 和 jquery-ui的插件
详解如何在 vue 项目里正确地引用 jquery 和 jquery-ui的插件

11.nextTick

  • nextTick可以让我们在下次 DOM 更新循环结束之后执行延迟回调,用于获得更新后的 DOM
  • nextTick主要使用了宏任务和微任务。根据执行环境分别尝试采用
    Promise
    MutationObserver
    setImmediate
    如果以上都不行则采用setTimeout
  • 定义了一个异步方法,多次调用nextTick会将方法存入队列中,通过这个异步方法清空当前队列

12.computedwatch区别

  • computed是计算属性,具有缓存性。
    当页面中有某些数据依赖其他数据进行变动的时候,可以使用计算属性computed
    用于依赖发生变化时,触发属性重新计算。
    Computed本质是一个具备缓存的watcher,依赖的属性发生变化就会更新视图。 适用于计算比较消耗性能的计算场景。
  • watch更多的是「观察」的作用,类似于某些数据的监听回调,用于观察props,$emit或者本组件的值,当数据变化时来执行回调进行后续操作。
    无缓存性,页面重新渲染时值不变化也会执行。
    Watch没有缓存性,更多的是观察的作用,可以监听某些数据执行回调。当我们需要深度监听对象中的属性时,可以打开deep:true选项,这样便会对对象中的每一项进行监听。这样会带来性能问题,优化的话可以使用字符串形式监听,如果没有写到组件中,不要忘记使用unWatch手动注销。
  • 应用场景:
    当我们要进行数值计算,而且依赖于其他数据,那么把这个数据设计为computed。
    如果你需要在某个数据变化时做一些事情,使用watch来观察这个数据变化。

13.对于MVVM的理解

  • MVVM 是 Model-View-ViewModel 的缩写
  • Model:表数据模型,因为它仅仅关注数据本身,不关心任何行为
  • View:用户操作界面,当ViewModel对Model进行更新的时候,会通过数据绑定更新到View
  • ViewModel :业务逻辑层,也是View和Model层的桥梁
  • 总结:总结: MVVM模式简化了界面与业务的依赖,解决了数据频繁更新。MVVM 在使用当中,利用双向绑定技术,使得 Model 变化时,ViewModel 会自动更新,而 ViewModel 变化时,View 也会自动变化。

14.Vue是如何实现双向绑定的?

  • Observer遍历数据对象,给所有属性加上settergetter,监听数据的变化
  • compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
  • Watcher 订阅者是 ObserverCompile 之间通信的桥梁,主要做的事情:
    在自身实例化时往属性订阅器 (dep)里面添加自己
    待属性变动 dep.notice()通知时,调用自身的 update()方法,并触发Compile中绑定的回调

    Vue2.x 响应式原理

  • vue实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty() 来劫持各个属性的settergetter,在数据变动时发布消息给订阅者,触发相应监听回调。当把一个普通 Javascript 对象传给 Vue实例来作为它的 data选项时,Vue将遍历它的属性,用Object.defineProperty()将它们转为getter/setter。用户看不到getter/setter,但是在内部它们让Vue追踪依赖,在属性被访问和修改时通知变化。
  • vue的数据双向绑定 将MVVM作为数据绑定的入口,整合Observer,CompileWatcher三者,通过Observer来监听自己的model的数据变化,通过Compile来解析编译模板指令(vue中是用来解析 {{}}),最终利用watcher搭起observerCompile之间的通信桥梁,达到数据变化 —>视图更新;视图交互变化(input)—>数据model变更双向绑定效果。
    原文参考:http://interview.poetries.top/

15.Vue的v-model双向绑定原理?

  • v-model本质就是一个语法糖,可以看成是value + input方法的语法糖。 可以通过model属性的prop和event属性来进行自定义。原生的v-model,会根据标签的不同生成不同的事件和属性

  • text 和 textarea 元素使用 value 属性和 input 事件

  • checkbox 和 radio 使用 checked 属性和 change 事件

  • select 字段将 value 作为 prop 并将 change 作为事件

  • 可以将v-model进行如下改写:

<input v-model="sth" />
//  等同于
<input :value="sth" @input="sth = $event.target.value" />
//这个语法糖必须是固定的,也就是说属性必须为value,方法名必须为:input。

16.ref的作用

  • 绑定到普通元素上:获取dom元素this.$refs.box
  • 绑定到子组件上:
    获取子组件中的data,this.$refs.box.msg
    调用子组件中的方法this.$refs.box.open()

17.导航守卫?

  • 全局守卫:beforeEach,afterEach
  • 局部守卫:
    beforeRouteEnter:因为当守卫执行前,组件实例还没被创建,不能获取组件实例的this
    beforeRouteUpdate: 在当前路由改变,但是该组件被复用时调用,可以访问组件实例 this
    beforeRouteLeave:导航离开该组件的对应路由时调用,可以访问组件实例 this
  • 每个守卫方法接收三个参数:
    to:目标路由对象
    from:准备要离开的路由
    next():进行管道中的下一个钩子。可传递参数有布尔值false,直接写路径'/'或{path:'/'},回调函数
  • 注意:后置钩子函数afterEach不会接受next函数也不会改变导航本身。
  1. 12道vue高频原理面试题,你能答出几道?

19.$route$router的区别

  • $route是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数。
  • $router是“路由实例”对象包括了路由的跳转方法,钩子函数等

20.<keep-alive></keep-alive>的作用是什么?

  • keep-alive可以实现组件缓存,当组件切换时,主要用于保留组件状态或避免重新渲染
  • 使用场景:比如有一个列表和一个详情,那么用户就会经常执行打开详情=>返回列表=>打开详情…这样的话列表和详情都是一个频率很高的页面,那么就可以对列表组件使用<keep-alive></keep-alive>进行缓存,这样用户每次返回列表的时候,都能从缓存中快速渲染,而不是重新渲染
  • 常用的两个属性include/exclude,允许组件有条件的进行缓存
  • 两个生命周期activated/deactivated,用来得知当前组件是否处于活跃状态

21.指令v-el的作用是什么?

  • 提供一个在页面上已存在的DOM元素作为Vue实例的挂载目标

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

  • 如果data是对象的话,由于对象是引用类型,组件被复用的话,就会创建多个实例。本质上,这些实例用的都是同一个构造函数。这样就会影响到所有的实例,所以为了保证组件不同的实例之间data不冲突,data必须是一个函数。

23.Vue中的key到底有什么用?

  • key是为Vue中的vnode标记的唯一id,通过这个key,我们的diff操作可以更准确、更快速。
  • diff算法的过程中,先会进行新旧节点的首尾交叉对比,当无法匹配的时候会用新节点的key与旧节点进行比对,然后超出差异。
  • 简单来说,主要是为了高效的更新虚拟DOM。另外vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,

24.说一下vue2.x中如何监测数组变化

  • 使用了函数劫持的方式,重写了数组的方法,Vuedata中的数组进行了原型链重写,指向了自己定义的数组原型方法。这样当调用数组api时,可以通知依赖更新。如果数组中包含着引用类型,会对数组中的引用类型再次递归遍历进行监控。这样就实现了监测数组变化。
  • 重写的数组API:pushpopshiftunshiftsplicesortreserve

25.你的接口请求一般放在哪个生命周期中

  • 接口请求一般放在mounted中,但需要注意的是服务端渲染时不支持mounted,需要放到created

26.Vue事件绑定原理说一下

  • 原生事件绑定是通过addEventListener绑定给真实元素的,组件事件绑定是通过Vue自定义的$on实现的

27.Vue模版编译原理知道吗,能简单说一下吗?

  • 简单说,Vue的编译过程就是将template转化为render函数的过程。
  • 会经历以下阶段:生成AST树(抽象语法树) --> 优化 --> codegen(代码生成器)
  • (1)首先解析模版,生成AST语法树(一种用JavaScript对象的形式来描述整个模板)。 使用大量的正则表达式对模板进行解析,遇到标签、文本的时候都会执行对应的钩子进行相关处理。
  • (2)Vue的数据是响应式的,但其实模板中并不是所有的数据都是响应式的。有一些数据首次渲染后就不会再变化,对应的DOM也不会变化。那么优化过程就是深度遍历AST树,按照相关条件对树节点进行标记。这些被标记的节点(静态节点)我们就可以跳过对它们的比对,对运行时的模板起到很大的优化作用。
  • (3)编译的最后一步是将优化后的AST树转换为可执行的代码

如何从真实DOM到虚拟DOM?

  • 涉及到Vue中的模板编译原理,主要过程:
  1. 将模板转换成ast 树,ast 用对象来描述真实的JS语法(将真实DOM转换成虚拟DOM)
  2. 优化树
  3. 将ast 树生成代码

28.Vue2.x和Vue3.x渲染器的diff算法分别说一下

  • Vue2.x中的虚拟dom 是进行全量的对比。
  • Vue3.0 中新增了静态标记(PatchFlag):在与上次虚拟结点进行对比的时候,值对比 带有 patch flag的节点,并且可以通过 flag的信息得知当前节点要对比的具体内容化
  1. Vue中hash模式和history模式的区别
  • 在最明显的显示上:hash模式的URL中会夹杂着#号,而history没有。
  • Vue底层对它们的实现方式不同:
    hash模式是依靠onhashchange事件(监听location.hash的改变)
    history模式是主要是依靠的HTML5 history中新增的两个方法,pushState()可以改变url地址且不会发送请求,replaceState()可以读取历史记录栈,还可以对浏览器记录进行修改。
window.onhashchange = function(event){
 // location.hash获取到的是包括#号的,如"#heading-3"
 // 所以可以截取一下
  let hash = location.hash.slice(1);
}
  • 当真正需要通过URL向后端发送HTTP请求的时候,比如常见的用户手动输入URL后回车,或者是刷新(重启)浏览器,这时候history模式需要后端的支持。因为history模式下,前端的URL必须和实际向后端发送请求的URL一致,例如有一个URL是带有路径path的(例如www.libai.wang/blogs/id),如果后端没有对这个路径做处理的话,就会返回404错误。所以需要后端增加一个覆盖所有情况的候选资源,一般会配合前端给出的一个404页面。
  1. 说说你对 proxy 的理解
    vue的数据劫持有两个缺点:
  • 无法监听通过索引修改数组的值的变化
  • 无法监听object 也就是对象的值的变化
    所以vue2.x中才会有$set 属性的存在
    proxyes6中推出的新 api,可以弥补以上两个缺点,所以 vue3.x版本用 proxy 替换object.defineproperty

31.Vue 的响应式原理中 Object.defineProperty 有什么缺陷?

  • Object.defineProperty 无法监控到数组下标的变化,导致通过数组下标添加元素,不能实时响应;
  • Object.defineProperty只能劫持对象的属性,从而需要对每个对象,每个属性进行遍历,如果,属性值是对象,还需要深度遍历。Proxy 可以劫持整个对象,并返回一个新的对象
  • Proxy 不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性

32.Proxy 相比于 defineProperty 的优势?

  • 数组变化也能监听到
  • 不需要深度遍历监听



作者:Angel_6c4e
链接:https://www.jianshu.com/p/7c3dc76178bf
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值