前端面试官常问的问题和答案(高频Vue面试题及答案汇总)
1、MVC和MVVM的理解
M:model(数据模型),V:view(视图),C:controller(逻辑处理),VM:(连接model和view)
MVC:单向通信。必须通过controller来承上启下。
MVVM:数据双向绑定,数据改变视图,视图改变数据。
2、数据双向绑定原理
答:通过数据劫持结合发布—订阅模式,通过Object.defineProperty()为各个属性定义get、set
方法,在数据发生改变时给订阅者发布消息,触发相应的事件回调。
3、vue生命周期
Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。通俗说就是 Vue 实例从创建到销毁的过程,就是生命周期。
- beforeCreate:创建前。此时,组件实例刚刚创建,还未进行数据观测和事件配置,拿不到任何数据。
- created:创建完成。vue 实例已经完成了数据观测,属性和方法的计算(比如props、methods、data、computed和watch此时已经拿得到),未挂载到DOM,不能访问到el属性,el属性,ref属性内容为空数组常用于简单的ajax请求,页面的初始化。
- beforeMount:挂载前。挂在开始之前被调用,相关的render函数首次被调用(虚拟DOM)。编译模板,把data里面的数据和模板生成html,完成了el和data 初始化,注意此时还没有挂在html到页面上。
- mounted:挂载完成。也就是模板中的HTML渲染到HTML页面中,此时可以通过DOM API获取到DOM节点,$ref属性可以访问常用于获取VNode信息和操作,ajax请求,mounted只会执行一次。
- beforeUpdate:在数据更新之前被调用,发生在虚拟DOM重新渲染和打补丁之前,不会触发附加地重渲染过程。
- updated:更新后。在由于数据更改导致地虚拟DOM重新渲染和打补丁之后调用,
beforeDestroy;销毁前。在实例销毁之前调用,实例仍然完全可用。(一般在这一步做一些重置的操作,比如清除掉组件中的定时器 和 监听的dom事件)
- destroyed:销毁后。在实例销毁之后调用,调用后,vue实列指示的所有东西都会解绑,所有的事件监听器会被移除。
其他:
activated:在keep-alive组件激活时调用
deactivated:在keep-alive组件停用时调用
4、为什么使用虚拟DOM(常问)
- 创建真实DOM的代价高:真实的 DOM 节点 node 实现的属性很多,而 vnode 仅仅实现一些必要的属性,相比起来,创建一个 vnode 的成本比较低。
- 触发多次浏览器重绘及回流:使用 vnode ,相当于加了一个缓冲,让一次数据变动所带来的所有 node 变化,先在 vnode 中进行修改,然后 diff 之后对所有产生差异的节点集中一次对 DOM tree 进行修改,以减少浏览器的重绘及回流。
- 虚拟dom由于本质是一个js对象,因此天生具备跨平台的能力,可以实现在不同平台的准确显示。
- Virtual DOM 在性能上的收益并不是最主要的,更重要的是它使得 Vue 具备了现代框架应有的高级特性。
5、v-if 和 v-show的作用和区别
无论v-show的值为true或者false,元素都会存在于html页面中; 而v-if的值为true时,元素才会
存在于html页面中。
v-show指令是通过修改元素的display属性来实现的。
v-if是动态地向DOM树内添加或者删除DOM元素,v-show是通过设置DOM元素的display样式属性控制
显隐的。
v-if是"真正的"条件渲染,因为它会确保在奇幻过程中条件快内的事件监听器和子组件适当的销毁和
重建。
v-if也是惰性的,如果在初始渲染时条件为假,则什么也不做,直到条件第一次变为真时,才开始渲染
条件块。
v-show不管初始条件是什么,元素总会背渲染,并且只是简单地基于css进行切换。
一般来说,v-if有更高的切换开销,而v-show则有更高的初始渲染开销
因此,如果需要非常频繁地切换,使用v-show比较好,如果在运行时条件不会改变时,用v-if比较好
切换时生命周期函数的执行
v-if
初始渲染
初始值为false时,不会渲染(所谓惰性),生命周期函数不会执行
初始值为true时,组件开始渲染,依次执行beforCreate,created,beforeMount,mounted等生命周期
切换时
从false到true:依次执行beforeCreate,created,beforeMount,moynted
从true到false:依次执行beforeDestroy,destroyed
v-show
初始渲染
无论初始状态为true还是false,组件都会渲染,依次执行beforeCreate,created,beforeMount,mounted
切换
基于css进行切换,对生命周期函数无影响.
6、用过vue的哪些组件?
1、vue-route
2、axios
3、vuex
4、Element UI
5、swiper
6、vue-echarts
7、vue-video-player
8、vue-photo-preview等等
7、vue-router 的模式有?
1、hash模式,用URL hash值来做路由,支持所有浏览器;该模式实现的路由,在通过链接后面
添加““#”+路由名字”。
2、history模式,由h5提供的history对象实现,依赖H5 History API和服务器配置。
3、abstract模式,支持所有JS运行环境,如Node服务器端,如果发现没有浏览器的API,路由会
自动强制进入该模式。
8、组件之间如何传值
Vue父子 组件之间传值
子组件通过props来接受数据和通过$emit来触发父组件的自定义事件
兄弟组件之间的传值
建一个公共组件bus.js.。传递方通过事件触发bus.$emit。接收方通过在mounted(){}生命周期
里触发bus.$on。
可以通过VUEX 来跨组件传参
父孙传值 `$attrs`(向下)`$listeners`(向上)
祖先和子孙传值provide/inject
获取父组件实例this.$parent
9、路由之间如何传参
- 通过router-link路由导航跳转传递
<router-link to=`/a/${id}`>routerlink传参</router-link>
- 跳转时使用push方法拼接携带参数。
this.$router.push({ path: `/getlist/${id}`, })
- 通过路由属性中的name来确定匹配的路由,通过params来传递参数
this.$router.push({ name: 'Getlist', params: { id: id } })
- 使用path来匹配路由,然后通过query来传递参数。
this.$router.push({ path: '/getlist', query: { id: id } })
注意:query有点像ajax中的get请求,而params像post请求。
params在地址栏中不显示参数,刷新页面,参数丢失,
其余方法在地址栏中显示传递的参数,刷新页面,参数不丢失。
10、VUEX
原理:Vuex是专门为vue.js应用程序设计的状态管理工具。
1、state 保存vuex中的数据源,通过this.$store.state获取
2、getters 用于监听state中的值的变化,返回计算后的结果。getter的返回值会根据它的依赖
被缓存起来
3、mutations 是修改store中的值得唯一方式 //this.$store.commit('add')
4、action 官方建议提交一个actions,在actions中提交mutations再去修改状态值。
this.$store.dispatch('add')
5、modules 模块化
11、如何解决vuex页面刷新数据丢失问题?
原因:因为vuex里的数据是保存在运行内存中的,当页面刷新时,页面会重新加载vue实例,vuex里面的数据就会被清空。
解决方法:将vuex中的数据直接保存到浏览器缓存中。(一般是用sessionStorage)
12、computed和watch的区别?
computed值有缓存、触发条件是依赖值发生更改、 watch无缓存支持异步、监听数据变化
computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生
改变,下一次获取 computed 的值时才会重新计算 computed 的值;
watch: 更多的是观察的作用,支持异步,类似于某些数据的监听回调 ,每当监听的数据变化时都
会执行回调进行后续操作;
computed应用场景:需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以
利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
watch应用场景:需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch
选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结
果前,设置中间状态。这些都是计算属性无法做到的。
13、route和router的区别
- route:是路由信息对象,包括“path,parms,hash,name“等路由信息参数。
- router:是路由实例对象,包括了路由跳转方法,钩子函数等。
14、vue中数据变了但是视图不跟新怎么解决?
vue不能检测以下变动的数组:
1、当你利用索引直接设置一个项时,vm.items[indexOfItem] = newValue
2、当你修改数组的长度时,例如: vm.items.length = newLength
对象属性的添加或删除
由于 Vue 会在初始化实例时对属性执行 getter/setter 转化过程,所以属性必须在 data 对象上存在才能让 Vue 转换它,这样才能让它是响应的。
解决办法:
使用 Vue.set(object, key, value) 方法将响应属性添加到嵌套的对象上
Vue.set(vm.someObject, ‘b’, 2) 或者 this.$set(this.someObject,‘b’,2) (这也是全局 Vue.set 方法的别名)
异步更新队列
在最新的项目中遇到了这种情况,数据第一次获取到了,也渲染了,但是第二次之后数据只有在再一次渲染页面的时候更新,并不能实时更新。
Vue 异步执行 DOM 更新。只要观察到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。如果同一个 watcher 被多次触发,只会被推入到队列中一次。
解决办法:
可在数据变化之后立即使用 Vue.nextTick(callback)。这样回调函数在 DOM 更新完成后就会调用
[点击打开视频讲解更加详细](【面试题】Vue的this.$nextTick方法,异步更新dom详解_哔哩哔哩_bilibili)
[点击打开文章详解](【面试题】Vue的this.$nextTick方法,异步更新dom详解_前端--末晨曦吖的博客-CSDN博客)
15、vue中data为什么是函数而不是对象?
vue组件是可复用的vue实例,一个组件被创建好之后,就可能被用在各个地方,而组件不管被复用了多少次,组件中的data数据都应该是相互隔离,互不影响的.
基于这一理念,组件每复用一次,data数据就会被复制一次,之后,当某一处复用的地方组件内data数据被改变时,其他复用地方组件的data数据不受影响。
如果data是对象的话,每当被复用是,复用的对象和源对象都会指向同一个内存地址(浅拷贝),互相之间相互影响,所以要是函数
16、vue中父子组件传值,父组件异步请求,子组件不能实时更新怎么解决?(vue中数据不能实时更新怎么解决?)
首先了解父子组件生命周期执行顺序
加载渲染数据过程
父组件 beforeCreate
父组件 created
父组件 beforeMount
子组件 beforeCreate
子组件 created
子组件 beforeMount
子组件 mounted
父组件 mounted
原因:因为生命周期只会执行一次,数据是要等到异步请求以后才能拿到,那么子组件的mounted钩子执行的时候,还没有拿到父组件传递过来的数据,但是又必须要打印出来结果,那这样的话,就只能去打印props中的默认值空字符串了,所以打印的结果是一个空字符串。
解决办法:
1、使用v-if控制组件渲染的时机
初始还没拿到后端接口的异步数据的时候,不让组件渲染,等拿到的时候再去渲染组件。使用v-if="变量"去控制,初始让这个变量为false,这样的话,子组件就不会去渲染,等拿到数据的时候,再让这个变量变成true,
举例:
data() { return { isTrue:false // 初始为false }; }, monted(){ this.$post.a.b.c.getData(res=>{ if(res.result){ this.isTrue = true } }) }
2、使用watch监听数据的变化
举例:
props: { tableData: { type: Array, default: [], }, }, watch: { tableData(val){ console.log(val) } },
3、使用VueX
Vue 路由跳转方式
router-link 标签跳转
this.$router.push()
this.$router.replace()
this.$router.go(n):(0:当前页,-1上一页,+1下一页,n代表整数)
17、Vue 中 for循环为什么加 key?
为了性能优化, 因为vue是虚拟DOM,更新DOM时用diff算法对节点进行一一比对,比如有很多li元素,要在某个位置插入一个li元素,但没有给li上加key,那么在进行运算的时候,就会将所有li元素重新渲染一遍,但是如果有key,那么它就会按照key一一比对li元素,只需要创建新的li元素,插入即可,不需要对其他元素进行修改和重新渲染。
key也不能是li元素的index,因为假设我们给数组前插入一个新元素,它的下标是0,那么和原来的第一个元素重复了,整个数组的key都发生了改变,这样就跟没有key的情况一样了。
18、vue路由守卫有哪些?
路由守卫分为三种 ——分别是:全局路由守卫、组件路由守卫、独享路由守卫。
全局守卫
全局守卫又分为全局前置守卫、和后置守卫
1. router.beforeEach((to,from,next)=>{})
回调函数中的参数,to:进入到哪个路由去,from:从哪个路由离开,next:函数,决定是否展示你要看到的路由页面。
从名字全局前置守卫不难理解,它是全局的,即对 整个单页应用(SPA) 中的所有路由都生效,所以当定义了全局的前置守卫,在进入每一个路由之前都会调用这个回调,那么如果你在回调中对路由的跳转条件判断出错,简单点就是死循环…因为你遗漏了某种路由跳转的情况,守卫会一直执行。所以在使用全局前置守卫的时候一定要判断清楚可能会出现的路由跳转的情况。
2.router.afterEach((to, from) => {})
只有两个参数,to:进入到哪个路由去,from:从哪个路由离。
理解了全局前置守卫,那么全局后置守卫也就那么一回事。全局后置守卫是整个单页应用中每一次路由跳转后都会执行其中的回调。所以多用于路由跳转后的相应页面操作,并不像全局前置守卫那样会在回调中进行页面的重定向或跳转。
组件内的守卫
组件路由守卫分为到达这个组件时,离开这个组件时
1. beforeRouteEnter:(to,from,next)=>{}——到达
to,from参数与上面使用方法一致。next回调函数略有不同。
2.beforeRouteUpdate:(to,from,next)=>{}——更新
3. beforeRouteLeave:(to,from,next)=>{}——离开
点击其他组件时,判断是否确认离开。确认执行next();取消执行next(false),留在当前页面。
路由独享的守卫
1. beforeEnter:(to,from,next)=>{}
与全局路由守卫用法一致,但是只能针对一个页面使用
19、vue常⽤的修饰符
.stop:等统⼀JavaScript中的event.stopPropagation(),防⽌事件冒泡
.prevent:等同于JavaScript中的event。preventDefault(),防⽌执⾏预设的⾏为(如果事件可
取消,则取消该事件,⽽不停⽌事件的进⼀步传播);
.capture:与事件冒泡的⽅向相反,事件捕获由外到内
.self:只会触发⾃⼰范围内的事件,不包含⼦元素;
.once:只会触发⼀次。
20、vue的两个核心点
数据驱动:ViewModel,保证数据和视图的⼀致性
组件系统:应⽤类UI可以看做全部是由组件树构成的
21、delete和Vue.delete删除数组的区别
delete只是被删除的元素变成了empty/undefined其他的元素的键值还是不变。
Vue.delete直接删除了数组 改变了数组的键值
22、vue常⽤的UI组件库
Mint UI,element,VUX
原文地址:https://tangjiusheng.cn/web/894.html