前端面试笔记之vue篇
- 前言
- 1.数据双向绑定原理⭐⭐⭐
- 2. VUE生命周期⭐⭐⭐
- 3.组件之间如何传值⭐⭐⭐
- 4.路由之间如何传参⭐
- 5.谈一谈VUEX⭐⭐
- 6.如何解决VUEX页面刷新数据丢失问题?⭐⭐
- 7.computed和watch的区别?⭐⭐⭐
- 8.如何封装axios?⭐
- 9.Route和router的区别⭐
- 10.v-show和v-if的区别⭐
- 11.vue中数据变了但是视图不跟新怎么解决?⭐
- 12.vue中data为什么是函数而不是对象?⭐⭐
- 13.VUE中父子组件传值,父组件异步请求,子组件不能实时更新怎么解决?(VUE中数据不能实时更新怎么解决?)⭐⭐⭐
- 14.父子组件传参emit如何传多个参数?⭐
- 15.VUE路由跳转方式⭐⭐
- 16.条件渲染v-if 与 v-for 优先级⭐
- 17.VUE 中 $nextTick 作用与原理?⭐⭐⭐
- 18.VUE中 for循环为什么加 key?⭐⭐
- 19.VUE2和VUE3的区别?⭐⭐⭐
- 20.为什么VUE3不继续用$set?⭐
- 21.VUE路由中的history和hash的区别⭐⭐
- 22.Pinia和Vuex的区别⭐
- 23.Vue Diff算法⭐⭐
- 24. 动态组件 & 异步组件⭐
- 25. 事件修饰符⭐
- 其他
前言
这里是关于 vue面试的一些题,我整理了一些经常被问到的问题,出现频率比较高的问题,以及个人经历过的问题。如有不足之处,麻烦大家指出,持续更新中…(ps:一到三颗⭐代表重要性,⭐选择性了解,⭐⭐掌握,⭐⭐⭐前端需要知道的知识)
1.数据双向绑定原理⭐⭐⭐
答:通过数据劫持结合发布—订阅模式,通过Object.defineProperty()为各个属性定义get、set方法,在数据发生改变时给订阅者发布消息,触发相应的事件回调。
2. VUE生命周期⭐⭐⭐
概念:从创建、初始化数据、编译模板、挂载DOM、渲染-更新-渲染、卸载等一系列过程,称为为Vue 实例的生命周期。
vue2.0
- 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组件停用时调用
详情可看vue2.0官网生命周期钩子
vue3.0
- onBeforeMount
- onMounted
- onBeforeUpdate
- onUpdated
- onBeforeUnmount
- onUnmounted
详情可看vue3.0官网生命周期钩子
3.组件之间如何传值⭐⭐⭐
一、Vue父子 组件之间传值
- 子组件通过props来接受数据和通过
$emit
来触发父组件的自定义事件;
二、兄弟组件之间的传值
- 建一个公共组件bus.js.。传递方通过事件触发
bus.$emit
。接收方通过在mounted(){}生命周期里触发bus.$on
。
三、可以通过VUEX 来跨组件传参。
四、父孙传值 $attrs
(向下)$listeners
(向上)
五、 祖先和子孙传值provide/inject
六、获取父组件实例this.$parent
详情可看vue之组件的传参方式
4.路由之间如何传参⭐
- 通过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在地址栏中不显示参数,刷新页面,参数丢失,
其余方法在地址栏中显示传递的参数,刷新页面,参数不丢失。
详情请看Vue-router之简单的路由传参三种方法
5.谈一谈VUEX⭐⭐
原理:Vuex是专门为vue.js应用程序设计的状态管理工具。
构成:
- state:vuex的基本数据,用来存储变量,存放的数据是响应式的。
- mutations:提交更改数据,同步更新状态。
- actions:提交mutations,可异步操作。
- getters:是store的计算属性。
- modules:模块,每个模块里面有四个属性。
关于VUEX如何使用可以看VUE的传值问题
6.如何解决VUEX页面刷新数据丢失问题?⭐⭐
原因:因为vuex里的数据是保存在运行内存中的,当页面刷新时,页面会重新加载vue实例,vuex里面的数据就会被清空。
解决方法:将vuex中的数据直接保存到浏览器缓存中。
另一种方法:使用插件vuex-persistedstate
。
vuex-persistedstate可以将Vuex store的状态持久化存储到浏览器的localStorage或sessionStorage中,使得用户下次打开页面时能够继续使用之前的应用状态。
7.computed和watch的区别?⭐⭐⭐
computed值有缓存、触发条件是依赖值发生更改、 watch无缓存支持异步、监听数据变化
computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;
watch: 更多的是观察的作用,支持异步,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;
computed应用场景:需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
watch应用场景:需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
详情请看Vue的计算属性与methods、watch的区别
8.如何封装axios?⭐
这个根据自己项目实际情况来说,这篇文章写的不错可以看看Vue的axios的简单封装
9.Route和router的区别⭐
- route:是路由信息对象,包括“path,parms,hash,name“等路由信息参数。
- Router:是路由实例对象,包括了路由跳转方法,钩子函数等。
10.v-show和v-if的区别⭐
- v-if:组件的销毁和重建,更适合带有权限的操作,切换开大。如果开始条件为false则什么都不做,只有为true才会编译。
- v-show:css切换,隐藏显示更适合频繁切换。在任何情况下都会被编译,然后被缓存,而且dom元素会被保留。
11.vue中数据变了但是视图不跟新怎么解决?⭐
原因:
- 数组数据变动:使用某些方法操作数组,变动数据时,有些方法无法被vue监测,或者是数组长度的改变。
(push(),pop(),shift(),unshift(),splice(),sort(),reverse())不会改变原始数组,而是返回一个新的数组,视图不会自动更新。 - Vue 不能检测到对象属性的添加或删除。
- 异步更新队列:数据第一次的获取到了,也渲染了,但是第二次之后数据只有在再一次渲染页面的时候更新,并不能实时更新。
解决方法:数组长度变化可以用splice来修改,需要监听某个属性的变化用$set。
12.vue中data为什么是函数而不是对象?⭐⭐
官网中有这么一段介绍,详情可以看组件的复用
意思就是,在Vue中组件是可以被复用的,而当data是一个函数的时候,每一个实例的data都是独立的,不会相互影响了。
更详细的解释 ==>
13.VUE中父子组件传值,父组件异步请求,子组件不能实时更新怎么解决?(VUE中数据不能实时更新怎么解决?)⭐⭐⭐
首先了解父子组件生命周期执行顺序 ==>
加载渲染数据过程
父组件 beforeCreate -->
父组件 created -->
父组件 beforeMount -->
子组件 beforeCreate -->
子组件 created -->
子组件 beforeMount -->
子组件 mounted -->
父组件 mounted -->
原因:因为生命周期只会执行一次,数据是要等到异步请求以后才能拿到,那么子组件的mounted钩子执行的时候,还没有拿到父组件传递过来的数据,但是又必须要打印出来结果,那这样的话,就只能去打印props中的默认值空字符串了,所以打印的结果是一个空字符串。
解决办法:
- 使用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
}
})
}
- 使用watch监听数据的变化
举例:
props: {
tableData: {
type: Array,
default: [],
},
},
watch: {
tableData(val){
console.log(val)
}
},
- 使用VueX
14.父子组件传参emit如何传多个参数?⭐
子组件:
submit(){
this.$emit('g',1,2,3,4,5)
}
父组件
g(val1,val2,val3,val4,val5) {
console.log(val1,val2,val3,val4,val5)
}
15.VUE路由跳转方式⭐⭐
- router-link 标签跳转
- this.$router.push()
- this.$router.replace()
- this.$router.go(n):(0:当前页,-1上一页,+1下一页,n代表整数)
16.条件渲染v-if 与 v-for 优先级⭐
vue2.0文档是这么说的
vue2列表渲染指南
vue3.0文档是这么说的
vue3条件渲染
17.VUE 中 $nextTick 作用与原理?⭐⭐⭐
异步渲染、获取DOM、Promise等。
Vue 在更新 DOM 时是异步执行的,在修改数据后,视图不会立刻更新,而是等同一事件循环中的所有数据变化完成之后,再统一进行视图更新。所以修改完数据,立即在方法中获取DOM,获取的仍然是未修改的DOM。
$nextTick的作用是:该方法中的代码会在当前渲染完成后执行,就解决了异步渲染获取不到更新后DOM的问题了。
$nextTick 的原理: $nextTick本质是返回一个Promise 。
应用场景:
- 在created()里面想要获取操作Dom,把操作DOM的方法放在$nextTick中。
- 在data()中的修改后,页面中无法获取data修改后的数据,使用$nextTick时,当data中的数据修改后,可以实时的渲染页面
官网中是这么说的
18.VUE中 for循环为什么加 key?⭐⭐
为了性能优化优化diff算法, 因为vue是虚拟DOM,更新DOM时用diff算法对节点进行一一比对,比如有很多li元素,要在某个位置插入一个li元素,但没有给li上加key,那么在进行运算的时候,就会将所有li元素重新渲染一遍,但是如果有key,那么它就会按照key一一比对li元素,只需要创建新的li元素,插入即可,不需要对其他元素进行修改和重新渲染。
key也不能是li元素的index,因为假设我们给数组前插入一个新元素,它的下标是0,那么和原来的第一个元素重复了,整个数组的key都发生了改变,这样就跟没有key的情况一样了。
19.VUE2和VUE3的区别?⭐⭐⭐
- 双向数据绑定原理不同
Vue2 的双向数据绑定是利用Object.definePropert() 对数据进行劫持,结合发布订阅模式的方式来实现的。
Vue3 中使用Proxy API对数据代理。 - API 类型不同
Vue2 使用选项类型api。
Vue3 使用合成型api。 - 定义数据变量和方法不同
Vue2是把数据放到了data 中。
Vue3 就需要使用一个新的setup()方法。 - 生命周期不同
- 父子传参不同
- 指令与插槽不同
- 是否支持碎片
Vue2 不支持碎片。
Vue3 支持碎片,可以拥有多个根节点
20.为什么VUE3不继续用$set?⭐
$set的作用:在vue2.0中:使用对象和数组来定义数据,当需要向对象或数组中新增一个属性或元素,并希望它在更新 View 时响应式地更新,就需要使用 $set方法来完成。
vue2是用object.definedProperty来实现数据响应的,无法监听深层数据的变化。
Vue3 中使用Proxy对数据代理通过ref和reactive将值和对象类型变为响应式对象,这样对它的修改和添加就能被vue捕获到,从而实现页面的自动刷新。
参考官网响应式基础
21.VUE路由中的history和hash的区别⭐⭐
-
地址栏带不带"#"号
hash:http://localhost:8080/#/
history:http://localhost:8080/
-
都是利用浏览器的两种特性实现前端路由
history是利用浏览历史记录栈的API实现
hash是监听location对象hash值变化事件来实现 -
相同的url
history会触发添加到浏览器历史记录栈中,hash不会触发,
history需要后端配合,如果后端不配合刷新页面会出现404,hash不需要
hashRouter原理:通过window.onhashchange获取url中hash值
historyRouter原理:通过history.pushState,使用它做页面跳转不会触发页面刷新,使用window.onpopstate监听浏览器的前进和后退
详情可以看官网不同历史模式|Vue Router
22.Pinia和Vuex的区别⭐
- 支持选项式api和组合式api
- pinia没有mutations,只有state、getters、 actions
- pinia分模块不需要modules
- 支持TypeScript
- 自动化代码拆分
- pinia体积更小
- pinia可以直接修改state数据
参考https://github.com/vuejs/rfcs/pull/271
23.Vue Diff算法⭐⭐
Diff 算法是指对比两个树形结构的不同,这种算法最早有应用于文本编辑器的对比修改,而在前端领域中,则用于对比真实 DOM 树和虚拟 DOM 树。
在 Vue 中,当发现需要更新数据时,Vue 会生成一棵新的 virtual(虚拟) DOM 树,然后将该树与旧的 virtual DOM 树进行对比,并将差异更新到真实的 DOM 树上。这个对比的过程就是一个 Diff 算法。
具体来说,Vue 中的 Diff 算法是指对比两个 virtual DOM 树的差异,并且尽量找出一种最小的变更方式来完成从旧的 virtual DOM 到新的 virtual DOM 的转换。这种方式非常高效,因为不需要每次对真实 DOM 进行重新渲染,而只需要找出变更的部分,更新到真实 DOM 上即可。
在比较两个 virtual DOM 树的差异时,Vue 的 Diff 算法采用的是递归遍历,先比较节点的类型,如果不同,则不再遍历其子节点,直接判断为需要替换当前节点;否则比较节点的属性和子节点,依照节点的属性、子节点排序等,找到最优的更新方式。
Vue 中的优化策略
在 Vue 的 diff 算法中引入了一些优化策略,以减少不必要的 diff 操作,提高 diff 性能,具体来说,有以下几点:
- 模板编译时静态节点标记
Vue 在模板编译时,会对那些不需要变更的静态节点做标记,这样在后续渲染时,就可以省略对这些节点进行 diff 操作,减少 diff 的复杂度,提高性能。 - 列表遍历的 key 值优化
在 Vue 中,当对一个列表进行遍历时,我们通常会为每个子元素指定一个 key 值来唯一标识它们,以便于后续的 diff 操作。在这个过程中,Vue 会通过记录 key 值来对比新旧节点是否发生了变化,如果没有变化,则会保留该节点对应的状态,避免不必要的重复渲染。 - 相同节点的合并优化
在 diff 算法中,如果新旧节点是相同节点(即两个节点都是相同的标签和属性),并且都不需要更新子节点,则认为这两个节点是相同的,可以直接跳过对该节点的 diff 操作,提高 diff 性能。
Vue 的 diff 算法是一种高效的前端响应式实现方式,能够快速地对比新旧节点间的差异,并快速更新页面视图,提升了用户体验和程序性能。
24. 动态组件 & 异步组件⭐
动态组件和异步组件都是为了实现组件的动态渲染和按需加载,可以提高应用的性能和灵活性。
动态组件 :渲染组件时,根据某个条件动态地选择组件。
异步组件:组件的加载和渲染过程分成两部分进行,即先加载组件的代码和依赖,等加载成功后再将其渲染到页面上。这样可以避免在初始加载时一次性加载所有组件的代码和依赖,从而提高页面的性能和响应速度。在Vue中,可以使用工厂函数和组件的异步加载特性来实现异步组件的加载。
参考官网
vue2异步组件
vue3异步组件
25. 事件修饰符⭐
.stop:阻止单击事件继续传播
.prevent:提交事件不再重载页面
.capture :添加事件监听器时使用事件捕获模式
.self :当前元素自身时触发处理函数
.once :只会触发一次
.passive :提升移动端的性能。
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成 -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>
详细请看官网事件修饰符
其他
关于前端其他面试题可以看这个
前端面试题(附答案)完善中……