2022vue面试题收集

270 阅读20分钟

1,什么是事件委托?

答:所谓事件委托,就是将原本应该在当前元素绑定的事件,放到它的祖先元素上,让祖先元素来委托处理。

 

2,vue3响应式原理?

vue3中使用Proxy对象来代替vue2中基于object.defineProperty,消除vue2中基于object.definePropety所存在的一些局限,比如无法监听数组索引,length属性等;

当数据改变时,引用数据的函数会自动重新执行;

vue2的源码-数据代理-数据劫持-发布订阅-绑定关系

数据代理-this.name就可以设置this.$data.name的属性-数据代理

把$data中的数据代理给了this这个实例对象,

vue2如果多层次就要递归;

  • 遍历属性,对每一个属性的值用Object.defineProperty进行getter和setter的改造;

 

3,vue3生命周期作用?

答:1,每个vue组件实例被创建后经过一系列初始化步骤,比如,它需要数据观测,模板编译,挂载实例到dom上,以及数据变化时更新dom,整个过程会运行叫做生命周期钩子的函数,以便用户在特定阶段有机会添加他们自己的代码;

2,生命周期可以氛围8个阶段,创建前后,载入前后,更新前后,销毁前后,以及一些特殊场景的生命周期,vue3中新增了3个用于调试和服务端渲染场景;

3,onbeforeCreate,在实例初始化之后,数据观测和event/watcher事件配置之前被调用;

compositon API setup函数会优先处理,beforeCreate

init Options API ,created,通常用于插件开发中执行一些初始化任务,比如调用api;

created:组件初始化完毕,可以访问各种数据,获取接口数据等;

beforeMount:在这个阶段已经编译好模板,并将data里面的数据和模板生成html,但此时还未挂载html到页面上;

mounted:当数据发生改变的时候vue会re-render 并且patch做对比;dom已经创建,可以用于获取访问数据的dom元素,访问子组件等。

beforeUpdate:此时view层还未更新,可用于获取更新前各种状态;

setup:创建实例前,vue3要在setup里面写钩子函数,setup先执行,一个滞后的东西写在setup里面没有意义;写生命周期是为了在前面把预设埋上,在需要的时候才去用;实例已经创建了没必要去写beforeCreate和created;由于setup发生的最早;

updated:完成view层的更新,更新后,所有状态已是最新;

更新组件比较少用可以用watch监听某一个特别字段;

beforeUnmount:实例被销毁前调用,可用于一些定时器或订阅的取消;

onUnmounted:销毁一个实例,可清理它与其他实例的连接,解绑它的全部指令及事件监听器;

 

5,nextTick的使用和原理,作用,属于微任务还是宏任务?

答:(1)nextTick是等待下一次dom更新刷洗的工具方法;

(2)vue有个异步更新策略,意思是如果数据变化,vue不会立刻更新dom,而是开启一个队列,把组件更新函数保存在队列中,在同一事件循环中发生的所有数据变更会异步的批量更新。这一策略导致我们对数据的修改不会立刻体现在dom上面,此时如果想要获取更新后的dom状态,就需要使用nextTick.开发时,有2个场景会用到nextTick:

(1)created中想要获取dom时;

(2)响应式数据变化后获取dom更新后的状态,比如希望获取列表更新后的高度;

(3)nextTick签名如下:function nextTick(callback?:()?:()=>void):promise所以我们只需要在传入的回调函数中访问最新dom状态即可,或者我们可以await nextTick()方法返回的promise之后做这件事;

(4)在vue内部,nextTick之所以能够让我们看到dom更新后的结果,是因为我们传入的callback会被添加到队列刷新函数(flushSchedulerQueue)的后面,这样等队列内部的更新函数都执行完毕,所有dom操作也就结束了,callback自然能够获取到最新的dom值;

nextTick即有可能是微任务,也有可能是宏任务,从优先去Promise和MutationObserver可以看出nextTick优先微任务,其次是setImmediate和setTimeout宏任务。

7,diff算法,递归?

答:(1)vue的diff算法称为patching算法,它由snabbdom修改而来,虚拟dom想要转化为真是的dom就要通过patch方法转换;

(2)vue中的diff执行的时刻是组件内响应式数据变更触发实例执行其更新函数时,更新函数会再次执行render函数获取最新的虚拟dom,然后执行patch函数,并传入新旧2次虚拟dom,通过比对两者找到变化的地方,最后将其转化为对应的dom操作;

(3)patch过程是一个递归的过程,遵循深度优先,同层比较的策略;首先判断2个节点是否为相同同类节点,不同则删除重新创建,如果双方都是文本则更新文本内容;如果双发都是元素节点则递归更新子元素,同事更新元素属性;更新子节点时又分了几种情况:

①:新的子节点是文本,老的子节点是数组则清空,并设置文本;

②:新的子节点是文本,老的子节点是文本则直接更新文本;

③:新的子节点是数组,老的子节点是文本则清空文本,并创建新子节点书中的子元素;

④:新的子节点是数组,老的子节点也是数组,那么比较两组子节点

(5)vue3中引入的更新策略,编译期优化patchFlags,block等;

 

8,怎么给子组件做双向绑定;

(1)父组件将数据传递给子组件通过 props 实现;而子组件将数据传递给父组件通过事件来实现,在子组件中定义一个事件,在该事件中传递值,由父组件中监听这个事件。通过这种方式实现父子组件双向绑定的效果最常见。

(2)在子组件中指定 model 变量,父组件中通过 v-model 指令来实现双向绑定,通过这种方式父组件无需监听子组件的事件。

 

9,keep-alive作用和原理?

答:keep-alive 实现了组件的缓存,当组件切换时不会对当前组件进行卸载。常用的2个属性 include / exclude 以及max属性,2个生命周期 activated / deactivated,以及LRU算法。

include 对哪些进行缓存

exclude 对哪些不进行缓存

max 最多缓存多少个

keep-alive是一个组件,这个组件中有三个属性,分别是include、exclude、max,在created中创建缓存列表和缓存组件的key列表,销毁的时候会做一个循环销毁清空所有的缓存和key,当mounted时会监控include 和 include属性,进行组件的缓存处理。如果发生变化会动态的添加和删除缓存。渲染的时候会去拿默认插槽,只缓存第一个组件,取出组件的名字,判断是否在缓存中,如果在就缓存,不在就直接return掉,缓存的时候,如果组件没有key,就自己通过组件的标签、key和cid拼接一个key。如果该组件缓存过,就直接拿到组件实例。如果没有缓存过就把当前的vnode缓存,和key做一个对应关系。这里面有一个算法叫LRU,如果有key就不停的取,如果超限了就采用LRU进行删除最近最久未使用的,从前面删除,LRU就是将当前使用的往数组的后面移,在最前面的就是最久未使用的。

(1)开发中缓存组件使用keep-alive组件,keep-alive是vue内置组件,包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们,这样在组件切换过程中将状态保留在内存中,防止重复渲染;

(2)vue3中结合vue-router时变化较大,之前是keep-alive包裹‘router-view',现在需要反过来用’router-view'包裹‘keep-alive';

(3)缓存后如果要获取数据,解决方案有2种:

beforeRouteEnter:在有vue-router项目,每次进入路由的时候,都会执行’beforeRouteEnter'

beforeRouteEnter(to,from,next){

next(vm=>{

console.log(vm)

vm.getData()//获取数据

})

),

actived:在‘keep-alive'缓存的组件被激活的时候,都会执行’actived'钩子,

activated(){

this.getData()

}

(4)keep-alive是一个通用组件,它内部定义了一个map,缓存创建过的组件实例,它返回的渲染函数内部会查找内嵌的组件对应组件的vnode,如果该组件在map中存在就直接返回它,由于component的is属性是个响应式数据,因此只要它变化,keep-alive的render函数就会重新执行;

10,vue2和vue3的区别;

(1):使用缓存,利用缓存cookies,在每次登录的时候将token存入state的同时将token进行缓存

(2):

11,插槽sloat?

那么Slot到底是什么呢?Slot其实是一个接受父组件传过来的插槽内容,然后生成VNode并返回的函数。

我们一般是使用 <slot></slot> 这对标签进行接受父组件传过来的内容,那么这对标签最终编译之后是一个创建VNode的函数,我们可以叫做创建插槽VNode的函数。

组件的核心是它能够产出一坨VNode。对于 Vue 来说一个组件的核心就是它的渲染函数,组件的挂载本质就是执行渲染函数并得到要渲染的VNode,至于什么data/props/computed 这都是为渲染函数产出 VNode 过程中提供数据来源服务的,最关键的就是组件最终产出的VNode,因为这个才是需要渲染的内容。

12,什么是MVVM?

答:是一种设计模式,model就是数据-它是一切的核心,所有数据都存于model-vue;

view就是视图-ui页面-html页面-数据最终要体验的页面上;

viewModel-vm-

用object.definePropertey-里面的get和set方法

vue2的源码-数据代理-数据劫持-发布订阅-绑定关系

数据代理-this.name就可以设置this.$data.name的属性-数据代理

把$data中的数据代理给了this这个实例对象

13,什么是VUEX?

Vuex这个东西我比较熟,但是并不一定所有的搜需要状态共享。

(1)三大核心版块;

(2)state-放置状态的位置-所有需要共享的状态只能放在state中

(3)mutation-改状态必须通过mutations-只写同步代码,可以写异步,形成数据快照-devtools打印,纯函数概念-输入参数固定-输出结果固定

(4)actions-动作-异步放在action中-得到状态的更改也必须通过mutation

14,vuex怎么做持久化管理?

(1)使用缓存,利用缓存cookies,在每次登录的时候将token存入state的同时将token进行缓存

2)vuex-persistedstate,一个实现持久化的工具

原理:vuex-persistedstate做了这样的事情,它帮我们将store里面的state映射到了本地环境中。这样一来,我通过提交mutation改变的state,会动态的去更新local里面对应的值。

(3)就是最直接的直接自己将数据存入到localStorage (不推荐):会有很多问题,做不到真正的响应式,而且比较麻烦

15,vue2-vue3的变化?

vue3最大化的兼容了vue2,vue3里面可以写vue2,只是一个过渡。

最大的变化是组合式api的出现,vue2是选项式API

vue3推出的一个钩子,vue2的beforeCreate之前执行

setUp(){

const = ref(0)

return{count}

}

vue2是面向对象,this-实例对象

vue3不再用this-通过函数来声明

16,destory能不能在mounted里面写;

17,如何动态渲染嵌套表单?

  • 先写好各个可能会出现的表单或者自定义的组件,引入。
  • 此时后端可能会给到一个对象型数组,每个对象有要渲染组件的一个类型标识
  • 利用component is 动态组件,根据不同的组件类型标识,渲染展示不同的组件
  • 在利用组件的数据通信去收集各个子组件的数据

 

18,vue和react diff算法的区别?

答:vue和diff算法,都是不进行跨层级比较,只做同级比较;

不同点:1,vue进行diff时,调用Patch打补丁函数,一边比较一边给真真实的dom打补丁,vue对比节点时,当节点元素类型相同,类名不同时,认为是不同的元素,删除重新创建,而react认为是同类型的节点,进行修改操作;

2,vue列表对比的时候,采用从两端到中间的方式,旧集合和新集合两端各存在2个指针,两两进行比较,每次对比结束后,指针向队列中间移动;react则是从左往右一次对比,利用元素的index和lastIndex进行比较;

3,当一个集合把最后一个节点移动到最前面,react会把前面的节点依次向后移动,而vue只会把最后一个节点放在最前面,这样的操作来看,vue的diff性能是高于react的;

19,什么是虚拟dom?

答:(1)虚拟dom本身是一个js对象,只不过它是通过不同的属性去描述一个视图结构;

(2)通过引入vdom我们可以获得如下好处?

将真是的元素节点抽象成vnode,有效减少直接dom操作的次数,从而提高程序性能;直接操作dom是有限制的,比如:diff,clone等操作,一个真是元素上有许多的内容,如果直接对其进行diff操作,会去额外diff一些没必要的内容,同样的,如果需要进行clone那么需要将其全部内容进行复制,这也是没必要的,但是如果将这些操作转移到Js对象上面,那么就会变得简单了。

操作dom是比较昂贵的,频繁的操作dom容易引起页面的重绘和回流,但是通过抽象虚拟dom进行中间处理,可以有效减少dom的操作次数,从而减少页面的重绘和回流;

(3)方便实现跨平台

同一vnode节点可以渲染成不同平台上的对应的内容,比如:渲染在浏览器是dom元素节点,渲染在native变为对应的控件,可以实现SSR,渲染到webGl中等;

vue3中允许开发者基于VNODE实现自定义渲染器,以便于针对不同平台进行渲染;

(4)虚拟dom如何生成?在vue中我们常常会为组件编写模板-template,这个模板会被编译-compiler编译为渲染函数,在接下来的挂载过程中会调用render函数,返回的对象就是虚拟dom,但他们还不是真正的dom,所以会在后续的patch过程中进一步转化为dom;

(5)挂载过程结束后,vue程序进入更新流程。如果某些响应式数据发生变化,将会引起组件重新render,此时就会生成新的虚拟dom,和和上一次渲染结果diff就能得到变化的地方,递归调用Patch函数,从而转换为最小量的dom操作,高效更新视图。

 

20,造成内存泄漏的原因有哪些?

答:1、全局变量使用不当;2、闭包使用不当;3、延时器或定时器没有被清除;4、没有清理的DOM元素引用(dom清空或删除时,事件未清除)。

21,computed和watch的区别?

copmputed:获取一个getter函数,返回一个响应式对象,返回一个只读具有响应式返回值,有一个options对象,即可读又可写;关注响应式返回值,

watch:观察响应式数据源可以一个或多个;请求一个数据源回调函数当数据源发生变化;使用更灵活,特点:侦测变化,执行回调;

1、功能上:computed是计算属性,watch是监听一个值的变化,然后执行对应的回调。

2、是否调用缓存:computed中的函数所依赖的属性没有发生变化,那么调用当前的函数的时候会从缓存中读取,而watch在每次监听的值发生变化的时候都会执行回调。

3、是否调用return:computed中的函数必须要用return返回,watch中的函数不是必须要用return。

4、computed默认第一次加载的时候就开始监听;watch默认第一次加载不做监听,如果需要第一次加载做监听,添加immediate属性,设置为true(immediate:true)

5、使用场景:

        computed----当一个属性受多个属性影响的时候,使用computed-----购物车商品结算。

        watch–当一条数据影响多条数据的时候,使用watch-----搜索框.

6,computed使用:先计算结果,放入缓存,以后再用计算属性会直接从缓存中获取,如果计算属性中依赖的data数据发生了变化,计算会再次就散,再次放入缓存中,计算属性调用不需要写小括号;

22,vue-router使用模式hash和history区别和原理?

答:如果用户考虑url的规范那么就需要使用history模式,因为history模式没有#号,是个正常的url适合推广宣传。当然其功能也有区别,比如我们在开发app的时候有分享页面,那么这个分享出去的页面就是用vue或是react做的,咱们把这个页面分享到第三方的app里,有的app里面url是不允许带有#号的,所以要将#号去除那么就要使用history模式,但是使用history模式还有一个问题就是,在访问二级页面的时候,做刷新操作,会出现404错误,那么就需要和后端人配合让他配置一下apache或是nginx的url重定向,

hash路由模式原理

这个#就是hash符号,中文名哈希符或者描点。做前端的姑且这么称呼。然后哈希符后面的值叫做哈希值。

 

路由的哈希模式其实就是利用了window可以监听onhashchange事件。如果url中的哈希值发生变化,前端就可以做到监听并做一些响应,这样一来,即使前端没有发送http请求他也能够找到对应页面的代码进行按需加载。

优点

  • 兼容性强,兼容性达到了IE8
  • 除发送ajax和资源请求外不会发送其他多余请求
  • 改变#后的路径、不会自动刷新页面
  • 无需服务端进行配合

缺点

  • 访问路径上包含#,不美观
  • 对于需要锚点功能的需求会与当前路由机制发生冲突
  • 重定向操作时,后段无法获取url完整路径。

history路由模式原理

优点

  • 符合url地址规范, 不需要#, 使用起来比较美观
  • 可以使用history.state获取完整的路由信息
  • 后端可以获取到完整的路由信息

缺点

  • 兼容性只到IE10
  • 改变url路径后、会重新请求资源。
  • 若访问的路由地址不存在时、会报404,需服务端配合支持重定向返回统一的404页面

 

 

23,vue组件之间如何通信?

答:props,emit,emit,parent,attrs,ref,attrs,ref,root,eventbus s,vuex,childrenchildren和listeners被废弃了,

父子组件:通过Props/ emit(派发事件)/emit(派发事件)/parent(可以在子代中找Parent访问父组件)/父组件可以通过ref方式访问子组件/$atrrs

兄弟组件:可以通过共同的父组件parent/parent/root/enentbus/vuex

跨层级关系:eventbus(vue3移除)/vuex/provide+inject

 

24,项目权限管理如何做?

答:(1)权限管理一般需求是2个:页面权限和按钮权限;

(2)具体实现的时候分后端和前端2种方案:

前端方案会把所有路由信息在前端配置,通过路由守卫要求用户登录,用户登录后根据角色过滤出路由表。比如我会配置一个“asyncRoutes'数组,需要认真的页面在其路由的‘meta'中添加一个‘roles'字段,等获取用户角色之后取两者的交集,若结果不为空则说明可以访问,此过滤过程结束,剩下的路由就是该用户能访问的页面,最后通过router.addRoutes(accessRoutes)'方式动态添加路由即可;

后端方案会把所有页面路由信息存储在数据库中,用户登录的时候根据其角色查询得到其能访问的所有页面路由信息返回给前端,前端再通过addRoutes动态添加路由信息;

按钮权限的控制通常会实现一个指令,例如v-permissioni,将按钮要求通过值传给v-permission指令,在指令的mounted钩子中可以判断当前用户角色和按钮是否存在交集,有则保留按钮,无则移除按钮;

(3)纯前端方案的有点是实现简单,不要额外权限管理页面,但是维护起来问题比较大,有新的页面和角色需求就要修改前端代码重新打包部署;服务端方案就不存在这个问题,通过专门的角色和权限管理页面,配置页面和按钮 权限信息到数据库,应用每次登录时获取的都是最新的路由信息,可谓一劳永逸;

 

25,template的原理?

 

26,qiankun有没有了解过?

 

27,ifram标签

 

28,如何封装穿梭框

 

30,v-if和v-for哪个优先级高?

答:(1)实践中不应该把v-for和v-if放一起;

(2)在vue2中,v-for的优先级是高于v-if,把她们放在一起,输出的渲染函数中可以看出会先执行循环条件在判断条件,哪怕我们只渲染列表中一小部分元素,也得在每次重渲染的时候遍历整个列表,这回比较浪费,另外需要注意的是在vue3中则完全相反,v-if的优先级高于v-for,所以v-if执行时,它调用的变量还不存在,就会导致异常

(3)通常有2中情况下导致我们这样做:

①为了过滤列表中的项目(比如v-for="user in user" v-if="user.isActive")此时定义一个计算属性(比如‘activeUsers'),让其返回过滤后的列表即可,(比如’users.filter(u=>u.isActive));

②为了避免渲染本应该被隐藏的列表(比如:v-for="user in users" v-if="shouldShowUsers").此时把v-if移动到容器元素上(比如ul,ol)或者外面包一层‘template'即可)

(4)文档中明确指出永远不要把v-if和v-for同时用在一个元素上,显然这是

(5)源码里面关于代码生成的部分,能够清晰的看到是先处理v-if还是v-for,顺序上vue

产生了一些症状的不同,但是不管怎样都是不能把它们写在一起的。