vue高频面试题(2023),有回答思路,并且让你回答清晰

一、对MVC,MVP,MVVM的理解

三者都是项目的架构模式(不是类的设计模式),即:一个项目的结构,如何分层,不同层负责不同的职责。

1、MVC:

MVC的出现是用在后端(全栈时代)

M:model,模型:

主要完成业务功能,在数据库相关的项目中,数据库的增删改查属于模型(重点)。没有页面,是纯粹的逻辑

V:view,视图:

主要负责数据的显示(HTML+CSS,动态网页(jsp,含有html的php文件))页面的展示和用户的交互。

C:controller,控制器:

主要负责每个业务的核心流程,在项目中体现在路由以及中间件上(nodeJS中的routes文件夹)

2、MVP

MVP是把MVC中的C改成了P。主要限制了M和V之间不能直接通信(互相调用,传递数据)。M和V之间的通信必须经过P。

P:Presenter,表示器

主要用于连接M层、V层,完成Model层与View层的交互,还可以进行业务逻辑的处理。

3、MVVM:

MVVM是把MVP中P改成了VM。主要体现的是M和V之间的双向绑定。View的变动可以同步响应在Model,反之亦然。Vue就是一个MVVM的框架。准确来说,使用Vue框架完成项目时,使用的是MVVM模式。

VM:ViewModel

主要完成M和V的数据通信,并且是双向绑定。而 View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作 DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。

总结:所有MV*的项目的架构模式:都是为了完成项目代码的职责分工。

二、v-if和v-show区别

相同点:

都是用来控制dom元素的显示和隐藏。

不同点:

1、原理上:

v-if是 通过 添加和删除dom元素,来 控制dom元素的显示和隐藏。

v-show是 通过 控制dom元素样式的display属性的值,来 控制dom元素的显示和隐藏。

2、性能损耗

v-if:性能损耗主要体现在频繁切换时

v-show:性能损耗主要体现在首次。

3、应用场景:

v-if:用于切换不频繁的场景。

v-show:用户切换频繁的场景。

4、安全性:

v-if:安全性好。(如果dom元素不显示时,在elements里根本看不到)

v-show:安全性不好。(如果dom元素不显示时,在elements里依然可以看到,那么,懂程序 的人,即可以修改)

三、computed和watch的区别

1、相同点:

都可以监听数据。

2、不同的:

1)、概念:

computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,当依赖的属性值发生改变时,才会重新计算 computed 的值,默认是只读的(相当于getter函数),它也可以设置getter和setter函数来完成读和写。

watch:监听器,更多的是观察的作用,每当监听的数据变化时都会执行回调进行后续操作,它只能设置getter。watch默认只监听一层。如果要深度监听,让deep属性为true。

2)、作用:

computed:是为了显示而用,降低了模板上代码复杂度。

watch:属性变化的检测(相当于事件),当属性的值发生变化时,可以调用函数。主要处理异步以及开销比较大的操作。简单粗暴的理解:一个函数的执行时机时机

3)、依赖模板调用:

computed:只能在模板上使用。

watch:不能在模板上使用。

4)、是否异步:

computed:不能有异步,只能同步。

watch:可以有异步。

5)、立即执行:

computed:是立即执行。

watch:默认不是立即执行。如果要立即执行,增加属性:immediate。

补充一个 写代码的规范:

1、以后代码时,把函数定义和调用时机分开。

四、vue2的响应式原理

响应式:当js中的数据发生变化时,模板上会对应的发生变化。

1、数据劫持:

目的:当时数据发生变化时,vue框架能够感知到(劫持到)

怎么做的:利用ES5的Object.defineProperty()。当实例化组件时,vue框架内部会把定义在组件里data的所有数据(属性),进行遍历,给每个数据(属性)增加setter和getter函数【同时会做订阅】。当数据发生变化时,会调用setter函数。当获取数据时,会调用getter函数。

2、发布订阅者模式:

目的:当数据发生变化(其实就是调用setter函数)时,会发布给所有订阅者。

怎么做的:当vue组件实例化时,vue框架内部会扫描(阅读)模板,让模板上使用vue语法(指令,{{}}等)的dom元素去订阅对应的数据的变化。然后,当数据发生变化时【调用setter】,会更新所有订阅该数据的模板。

五、Vue3的响应式原理:

1、数据劫持:用Proxy替换了 Object.defineProperty。Proxy不需要循环,速度比Object.defineProperty快10倍。同时也解决了数组(vue2中用下标的方式改变数组不是响应式的)

2、发布订阅模式:

内部有个Track函数:相当于订阅。

内部有个Trigger函数:相当于发布。

六、双向绑定的原理

1、双向绑定:视图层变化时,模型层会变化。模型层变化时,视图层会变化。

2、原理: 1)、通过事件和属性完成。

事件完成的:视图层影响模型层

属性完成的是:模型层影响视图层(背后就是响应式原理)

3、vue针对官方标签使用v-model指令,v-model指令针对不同的官方标签使用不同事件和属性。

1)、针对文本框(单行和多行):value属性和input事件。 如果加上修饰符 lazy。事件变成了change事件。

2)、针对radio:使用的checked属性和change事件。同时,需要给radio加上value属性。

3)、针对checkbox:使用的checked属性和change事件。

3.1)、如果应用在多选时,需要给checkbox加上value属性。

3.3)、如果应用在单选时,不需要加。

4)、针对select:使用value属性和change事件。

七、单向数据流

1、单向数据流是什么:

单向数据流是指父组件可以修改子组件的数据,反之不行(子组件不能修改父组件的数据)。

2、vue框架的单向数据流:

vue框架的props是单向数据流。即:父组件可以修改子组件的props。子组件不能通过修改props的方式来修改父组件的数据(data),否则:

1)、造成数据混乱:如:一个父级组件的数据传递给多个子组件,某个子组件如果通过props修改的父级组件的数据,父级组件再修改其它子组件,其它子组件就会莫名其妙的被修改,造成数据混乱

2)、框架会给程序员报出一个警告(特指:父组件给子组件传递的props来自于父组件的数据)。

3、当父级的数据更新时,子组件的props也会随之更新。

八、keep-alive

keep-alive是vue提供的组件。

它有三个特性:

1)、组件作用:keep-alive 可以缓存组件及其状态(数据),避免了组件的频繁创建和销毁所带来的性能损耗。,一般结合路由和动态组件一起使用。

2)、组件属性:提供 include 和 exclude 属性。include 表示只有名称匹配的组件会被缓存,exclude 表示任何名称匹配的组件都不会被缓存 ,其中 exclude 的优先级比 include 高。两者都支持字符串或正则表达式

  这两个 prop 的值都可以是一个以英文逗号分隔的字符串、一个正则表达式,或是包含这两种类型的一个数组:
  它会根据组件的 name 选项进行匹配,所以组件如果想要条件性地被 KeepAlive 缓存,就必须显式声明一个 name 选项。
  
  //以下代码:表示缓存组件:MyFooter和MySearch
  
      
  

3)、keep-alive会触发两个钩子函数: activated 和 deactivated 。当组件被激活时,触发钩子函数 activated,当组件被移除时,触发钩子函数 deactivated。

九、SPA的理解:

1)、单页面应用的概念

SPA:single page application,单页面应用。

就是整个项目就只有一个html页面(文件),首次加载时,把所有的html,css,js全部加载下来。通过操作dom的删除和创建(添加)来完成页面的切换。

2)、单页面应用优缺点

优点:

1、单页应用相对服务器压力小。【因为:首次、或者只要HTML,CSS和JS加载完毕后,切换页面是不用再去服务器请求HTML,CSS和JS,而是直接操作DOM】

2、局部刷新,所以,用户体验好。【通过删除、添加、修改DOM的方式】 ​ 3、前后端分离 ​ 4、页面效果会比较炫酷(比如切换页面内容时的转场动画)

缺点:

1、不利于 SEO(Search Engine Optimization)。如:百度,360等搜索引擎收录。 ​ 2、初次加载时耗时多(可以使用路由懒加载解决) ​ 3、导航不可用,如果一定要导航需要自行实现前进、后退(vue-router做好了)。页面复杂度提高很多

4、容易造成CSS命名冲突。【用scoped或者BEM的方式解决】

十、请问你怎么理解虚拟DOM和diff算法

1、什么是虚拟dom和diff算法:

虚拟DOM: 用JS对象模拟的真实DOM,该js对象包含了真实DOM的所有属性和内容,

diff算法:用来比较两个虚拟DOM的不同之处,并在旧的虚拟dom树上打上标记。

2、虚拟DOM和diff算法的作用:

虚拟DOM和diff算法结合起来,用来提升性能。可以减少无效的DOM渲染,即:减少了无效的重排和重绘。

3、步骤(思路,流程)

1)、产生两个虚拟DOM树:newVDom,oldVDom。

2)、oldVDom和真实DOM保持一致

3)、数据变化时,影响的是(操作的是)newVDom

4)、操作newVDom后,通过diff算法对比newVDom和oldVDom的差异,并在oldVDom标注哪些节点要删除,哪些节点要增加,修改

5)、根据oldVDom(上的标记)操作真实的DOM,让真实Dom和oldVDom保持一致

4、diff算法的解释:

逐步解析newVdom的节点,找到它在oldVdom中的位置,

1)、如果找到了,则对比两个节点。

1.1)、两个节点内容不相同,那就打上修改标记,然后移动到下一个DOM元素。

1.2)、两个节点内容相同,直接移动到下一个的DOM元素,

2)、如果没找到,说明是新增节点,则新建一个节点插入到oldVDom。

3)、遍历完成之后如果oldVdom中还 没处理过的节点,则说明这些节点在newVdom中被删除了,打上删除标记。

十一、你对 Vue 项目进行哪些优化?

第一个方面:代码层面的优化 v-if 和 v-show 区分使用场景 computed 和 watch 区分使用场景 v-for 遍历必须为 item 添加 key,且避免同时使用 v-if 长列表性能优化 事件的销毁 图片资源懒加载 路由懒加载 第三方插件的按需引入 优化无限列表性能 服务端渲染 SSR or 预渲染 第二个方面:Webpack 层面的优化 Webpack 对图片进行压缩 减少 ES6 转为 ES5 的冗余代码 提取公共代码 模板预编译 提取组件的 CSS 优化 SourceMap 构建结果输出分析 Vue 项目的编译优化 第三个方面:基础的 Web 技术的优化 开启 gzip 压缩 浏览器缓存 CDN 的使用 使用 Chrome Performance 查找性能瓶颈

十二、vue中的v-for为什么要使用key

1、key的作用

1)、vue中使用了虚拟dom和diff算法,在使用diff算法进行对比两个虚拟dom树时,是通过标签名和key来标识一个元素的,这样才能区分(识别)不同的元素,提高对比的效率。所以,并不是说只是在v-for中使用key,是任何时候,如果希望唯一标识一个元素,那么,都可以使用key。

2)、vue在做过渡效果时(),如果两个切换的元素标签一样,而么有使用可以,那么,vue会认为是同一个元素,而不会出现切换效果。

2、为什么要使用key

1)、如果没有key,vue只能用标签名区分不同的DOM元素,如果两个DOM元素的位置进行了交换时,vue只会交互内容,而不会交换两个DOM元素。如下示例代码,仔细品读:




    
    
    
    Document


    
               

书籍列表:

       
               
  •               书名:{{book.name}}            
  •        
           

十三、组件的data为什么是个函数

简单回答:如果不是函数,那么,复用的组件的data共享同一块内存空间。

具体解释:组件的data必须是函数,而且要有返回object(就是vue2对象的data)。当组件复用时,会自动调用该函数,这样的话返回的新对象就是新开辟的空间。这样就保证了每个组件的data是独立的空间。而不会互相影响。即:组件的作用域(应该)是独立的。

十四、组件间通信:

1)、父子组件传值:

1)、父---子传:props,ref。vuex,pinia,provide 和 inject

……………………要说详细

2)、子-->父传:emit,vuex,pinia

……………………要说详细

2)、兄弟组件:

1)、子1---》父---》子2

2)、事件总线(event-bus)

原理:使用vue对象的$on和$emit完成。

具体做法:假设SonA给SonB传递数据。新建一个空的vue对象。在SonA里和SonB引入该vue对象。。

$emit:触发事件(在SonA)

$on:绑定事件(在SonB)

3)、vuex,pinia

3)、跨级:

provide 和 inject

vuex,pinia

十五、路由传参

1、vue-router有两种传参方式:params和query。

2、params

首先,需要在路由配置中使用动态路由匹配 {path: "/路径/:参数名",name:"路由名"}

1)、传【跳转时传】

//声明式:
1)、字符串写法
 
2)、对象写法
 
​
//编程式:
​
同上。

2)、接【路由的组件内部接收】

this.$route.params.参数名

3、query

1)、传

//声明式
//1)、字符串写法:

//2)、对象写法:

​
//编程式:
​
同上。
​

2)、接

this.$route.query.参数名

4、使用场景:

1)、params:多用于传递单个参数

2)、query:在传递多个参数时,建议使用query。

十六、$router和$route的区别

1)、$router是vue-router对象,是创建的vue-router对象,该对象具有路由相关api,如:push(),replace,go(),back(),forward。

2)、$route 是 匹配到的路由对象。当地址栏的路径发生变化时,会匹配某个路由配置。然后,vue-router对象就会在组件上去产生一个$route对象,来保存匹配到的路由的相关信息,包括:传递的参数,路由元信息等(如:path,params,query等等)

十七、vue两种路由模式的区别

1、外观上的区别:

1)、hash有#

2)、history没有#

2、原理上的区别:

1)、hash用的是锚点连接。背后使用location.href和window.hashchange事件。锚点连接自带历史记录。

2)、history背后用的是 history.pushState 来记录页面的历史记录。

3、跟后端有关的区别:

1)、hash和后端没有关系。

2)、history和后端有关系。当地址栏发生变化后,浏览器默认会发送请求给服务器(服务器上的资源:html,css,js,api接口,后端渲染的页面,等等),所以,当前后端不分离时:需要保证前端路径和后端的api或者后端渲染的页面不要重名。另外,前后端分离,前端服务器需要配置一个 针对404页面时,返回index.html。

十八、vue3选项式的生命周期

1、vue组件(实例)的生命周期是:

一个vue组件(实例)从创建,挂载,更新,销毁的整个过程。

2、vue组件(实例)的生命周期有:

四个阶段和八个钩子函数:

1)、创建阶段(数据挂载阶段):做响应式的处理和依赖注入,具体而言:是把data配置项的所有数据挂载到vue组件(实例)上,并做响应式。

在这个阶段的前后会分别调用:beforeCreate和created。

beforeCreate钩子函数里,不能通过vue组件(实例)拿到数据

created钩子函数里,vue组件(实例)上可以拿到数据

2)、模板渲染阶段:把数据渲染到模板上。

在这个阶段的前后会分别调用:beforeMount和mounted。

beforeMount钩子函数里,模板还没有渲染(模板上的内容还是就是程序员写的代码)

mounted钩子函数里,模板已经渲染(模板上的内容就是用户看到的)

3)、模板更新阶段(组件更新):当数据发生变化时,把新的数据渲染到模板上。

在这个阶段的前后会分别调用:beforeUpdate和updated。

beforeUpdate钩子函数里,数据是新的,模板是旧的

updated钩子函数里,模板是新的(模板已经被更新了)

4)、组件销毁阶段:卸载 组件里的watch、子组件、事件监听

强调:组件销毁阶段并不是把vue组件(实例)从内存中释放。

在这个阶段的前后会分别调用:beforeDestory(Vue3是beforeUnmount)和destoryed(vue3是unMounted)。

3、生命周期的使用场景:

1)、created:初始化数据、发送请求等操作在created里调用。

2)、mounted:也是可以发送请求的,如果有些初始化操作需要使用dom,那么,必须放在此处。

3)、beforeDestroy(beforeUnmount):清除定时器,清除事件总线等全局性的数据。

补充:

如果使用了keep-alive包裹组件,那么会经历两个钩子函数:activated,deactivated。组件就不会销毁了。既就是不会调用beforeUnmount和unMounted。

十九、父子组件生命周期钩子函数的调用顺序。

1、初始阶段:

父beforeCreate----》父created---》父beforeMount----》子beforeCreate---》子created--》子beforeMount--》子mounted --》父mounted。

2、更新阶段:

当父组件 数据传递给子组件了,当父组件的数据更新时,子组件也就会更新。

父beforeUpdate---->子beforeUpdate--->子的updated--->父的updated

3、销毁阶段:

父beforeUnmount---->子beforeUnmount--->子的unMounted--->父的unMounted

二十、事件总线的使用(兄弟组件传值)

二十一、路由守卫

1、什么是路由守卫

控制组件的跳转,对是否能够进入某个路径对应组件做限制。根据业务逻辑来判定是否可以进入某个组件。

什么时候使用路由守卫:

当进入某个路径,会有限制时。就需要使用路由守卫。

当进入路径时,需要完成通用的业务,也可以使用路由守卫,特别是全局路由守卫

2、路由守卫有哪些分类

1)、全局守卫

1)、前置钩子:beforeEach,当地址栏的路径发生变化时,会先调用该钩子函数,再进行进行路由匹配(路由匹配之前调用)。

2)、后置钩子:afterEach,当路由匹配成功后,先调用该函数,然后才创建组件(路由匹配成功之后)。

2)、路由独享守卫

1)、只有前置:beforeEnter:当匹配到某个指定的路由后,会先调用该函数,然后再创建组件。

3)、组件内部守卫

1)、前置:beforeRouteEnter:当路由匹配成功后,进入组件前,先调用该函数。

2)、路径更新,组件复用:beforeRouteUpdate: 当地址栏路径发生变化,但是进入的组件和上一个组件是同样的情况下,会调用先该函数。如:

3)、离开:beforeRouteLeave:当通过路由的方式离开某个组件前,会调用该函数。

3、路由钩子函数的参数:

to:想去哪个路由,to和$route是同样的对象

from:来自哪个路由对象,from和$route是同样的对象

next:下一步何去何从。

next(true):默认就是true,表示继续前行

next(false):不能前行

next(路径字符串或者对象):跳转到指定的路径。

二十二、vuex:

1、vuex是什么

vuex是一个状态(数据)管理工具,它能够完成组件之间的数据共享(组件的通信)

2、vuex的作用

1)、vuex能够保存全局数据(数据仓库),供整个应用使用

2)、vuex保存的数据是响应式的

3)、vuex保存的数据可以跟踪状态的变化

3、vuex的(核心概念)配置项:

1)、state : 数据仓库 ,存储所有的 共享数据 ,相当于vue组件里的data 2)、Getters : 在state的基础上 派生的数据, 相当于vue组件里 computed 3)、Mutations:修改state的数据时,用mutation,这与跟踪状态 有关系,只能有同步代码 4)、Action:解决mutation里只能有同步代码的问题,action里可以有异步代码

5)、modules:模块化

4、vuex的数据流转

vue组件 派发(dispatch)action。action里提交(commit)mutation,mutation里修改(mutate)state的数据,state的数修改后,会响应式渲染到模板上。

vue高频面试题(2023),有回答思路,并且让你回答清晰_第1张图片

5、模块化怎么使用。

1)、当项目比较大时,所有的全局数据存放在state里,会非常混乱,怎么办?使用module,把数据分门别类的进行处理,即:模块化。 每个模块是一个独立的store。然后由总体的store引入所有的分模块store。

2)、怎么解决(getters,mutations,actions)的重名

1)、

namespaced:true

2)、使用模块中getters,mutations,actions时,前面需要加上模块名:

格式:

模块名/getters或者mutations或者actions的名字

6、辅助函数:

mapState, mapGetters,mapMutations, mapActions

1)、作用:

简化代码:在组件里不需要使用$store 了。

2)、具体使用:

mapState, mapGetters 会映射到组件的computed上

mapMutations, mapActions 会映射到组件的methods里。

二十三、pinia和vuex的区别

一、相同点:

都是用于vue项目开发的状态管理工具

二、不同的:

1、根仓库:

vuex只有一个跟仓库

pinia可以有多个根仓库

2、配置项:

vuex常用的配置项有:state,mutations,actions,getters,modules

pinia常用的配置项有:state,actions,getters,plugins。没有smutations和modules

3、组合式api支持

vuex的组合式api支持的不彻底

pinia里完全支持vue3的组合式api,如果使用组合式api,那么,就不需要使用配置项了。

三、可以再补充细节的区别,查官网。

二十四、异步更新队列:

1)、异步更新队列的目的:

目的是提高性能,避免无效的重复的DOM更新。即:vue中更新数据后,并不会立即更新DOM,而是把数据引起的DOM更新放入到异步更新队列(去重了)里。等待下次事件循环(tick),并在两个tick之间进行UI渲染。这样程序员就不能在更改数据后,立即获取更新后的DOM,也不知道什么时候DOM能够更新。基于此,vue提供了nextTick函数。程序员把操作更新后DOM的代码放入到nextTick的回调函数里。由nextTick内部,在更新完DOM后,调用回调函数。

2)、异步更新队列的原理

vue更新DOM的思路。使用的就是异步更新队列,异步的实现使用了事件循环。使用如下API。

MutationObserver:这是HTML5新增的API。用于监视DOM变动的接口,它可以监听一个DOM对象上发生的子节点删除、属性修改、文本内容修改等

另外,考虑到,微任务比宏任务耗时少,浏览器的兼容性。所以,vue中延迟调用优先级如下: Promise > MutationObserver > setImmediate > setTimeout

二十五、$nextTick的理解:

1)、为什么用Vue.nextTick()

由于vue在状态被修改后,对应的UI渲染是异步的【内部使用了异步更新队列】,而在写vue的代码时,如果希望在状态修改后,使用新的dom时,那么,我们没有办法知道什么时候dom被更新了。vue框架为了适应这种场景,提供了Vue.nextTick()

其实你这样理解的话,更清楚:

首先,要知道,updated钩子函数,是在任何状态发生变化时,都会调用的钩子函数。此钩子函数里拿到的就是最新的dom。

其次,$nextTick(); 是某个数据引起的updated,而不是所有数据引起的updated。

2)、怎么使用。

在数据修改后,立即调用 Vue.nextTick()函数。给nextTick()函数传入回调函数。在回调函数里就能拿到最新的代码。【vue会在dom更新后调用 $nextTick传入的回调函数的代码】

this.msg = "hello"
this.$nextTick(()=>{
     操作更新后DOM的代码。
});

二十三、反向代理:

1、反向代理解决的问题:跨域

2、反向代理的原理:

把请求发到代理服务器(这个是同源),然后,由代理服务器请求真正的后端api服务器。浏览器看到的是同源,所以,不会出现跨域问题。

3、代码:

1)、代码写在何处:

vite脚手架:vite.config.js ----》server -----》proxy。 ​ vue-cli脚手架:vue.config.js ---> devserver --->proxy。

2)、代码的关键点:

2.1)、请求地址的开始标识。

2.2)、目标路径(真正的后端api服务器地址)

2.3)、是否去掉开始标识。

4、补充一下:如果上线后,需要在nginx配置。

你可能感兴趣的:(vue.js,前端,javascript)