2023年Vue3前端面试题考点
作者:小教学发布时间:2023-09-29分类:程序开发学习浏览:63
参考:前端面试题 — — vue篇_Jet_closer_burning的博客-CSDN博客
合集:2023年最全前端面试题考点HTML5+CSS3+JS+Vue3+React18+八股文+手写+项目+笔试_参宿7的博客-CSDN博客
[保姆级] Vue3 开发文档 - 掘金
Vue.js - 渐进式 JavaScript 框架 | Vue.js
框架为一二面,面试官尤其喜欢问为什么要用+怎么用
一到三颗⭐代表重要性,⭐选择性了解,⭐⭐熟悉,⭐⭐⭐掌握
目录
掌握⭐⭐⭐
Vue2和3
核心变化
双向数据绑定原理
Vue2 :数据劫持+发布订阅(Object.defineProperty,getter,setter(通知变化))
Vue3 :Proxy API对数据代理
性能:监听整个操作(不用遍历),不仅是读写
功能:可监听属性的增删
类型推导:TypeScript
API 类型
Vue2 :Options API选项类型(属性data、methods、computed)
Vue3 :Composition API合成型(函数定义组件逻辑,不同组件中可复用)
定义数据变量和方法
Vue2:数据放在data选项 中Vue3 :setup()方法
生命周期
Vue3
使用:先导入
合并:beforeCreate+created=setup
新增on:destroy->unmount
onErrorCaptured监听组件的统一报错:onErrorCaptured(error, component, details)
Fragment碎片组件:树
Vue2 不支持碎片Vue3 支持碎片,可以拥有多个根节点
非核心变化【但重要,可根据标题引导一波提问】
computed:有缓存(不必重算),依赖值发生变化触发
都支持函数和对象两种写法
vue2 函数
vue3 对象
watch:无缓存,单个监听值
vue2 属性(配置项:immediate,deep)
vue3 函数(监听的属性,(newVal, oldVal) => { ... },配置项(+flush,debug))
nextTick:DOM更新之后执行回调函数,返回一个Promise
vue2:作为vue的实例方法,this.$nextTick(()=>{})调用
vue3:独立的函数导入
this
vue2:this=组件实例($router,$ref)
vue3:{ proxy, appContext } = getCurrentInstance()
组件传值
父子组件:子组件props来接受,$emit触发父组件的自定义事件;
兄弟组件:一个公共组件bus.js.。传递方通过事件触发bus.$emit。接收方通过在mounted(){}生命周期里触发bus.$on
Vuex跨组件
Vuex
熟悉⭐⭐
掌握⭐⭐⭐
Vue2和3
核心变化
双向数据绑定原理
Vue2 :数据劫持+发布订阅(Object.defineProperty,getter,setter(通知变化))
Object.defineProperty的get方法用来获取值 set方法用来拦截设置值
把 data
对象里每个数据的读写转化成 getter
/setter
,当数据变化时通知视图更新。
var obj = {}; //定义一个空对象
Object.defineProperty(obj, 'val', {//定义要修改对象的属性
get: function () {
console.log('获取对象的值')
},
set: function (newVal) {
console.log('设置对象的值:最新的值是'+newVal);
}
});
obj.hello = 'hello world'
Vue3 :Proxy API对数据代理
性能:监听整个操作(不用遍历),不仅是读写
Proxy可以监听对象的整个操作,而不仅仅是属性的读写操作,这使得数据变化的检测更加高效。在Vue2中,需要递归地遍历对象的所有属性,而Proxy可以一次性监听整个对象的操作。
功能:可监听属性的增删
Proxy可以监听新增属性和删除属性的操作,而Object.defineProperty只能监听已有属性的读写操作
类型推导:TypeScript
由于Proxy是基于原生的Proxy对象实现的,所以可以更好地支持TypeScript等静态类型检查工具,提供更准确的类型推导和代码提示。
API 类型
Vue2 :Options API选项类型(属性data
、methods
、computed
)
选项在组件实例化后会被合并到组件实例中,可以在组件内部访问和使用。
<template>
<div>
<p>{{ message }}</p>
<button @click="changeMessage">Change Message</button>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello Vue!'
};
},
methods: {
changeMessage() {
this.message = 'New Message';
}
}
};
</script>
Vue3 :Composition API合成型(函数定义组件逻辑,不同组件中可复用)
合成型API不再依赖于特定的选项,而是通过一组函数来定义组件的逻辑,这些函数可以在不同的组件中复用。
<template>
<div>
<p>{{ message }}</p>
<button @click="changeMessage">Change Message</button>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
//ref函数来创建一个响应式的数据
const message = ref('Hello Vue!');
const changeMessage = () => {
message.value = 'New Message';
};
return {
message,
changeMessage
};
}
};
</script>
定义数据变量和方法
Vue2:数据放在data选项 中
Vue3 :setup()方法
生命周期
Vue3
使用:先导入
// vue3
<script setup>
import { onMounted } from 'vue'; // 使用前需引入生命周期钩子
onMounted(() => {
// ...
});
// 可将不同的逻辑拆开成多个onMounted,依然按顺序执行,不会被覆盖
onMounted(() => {
// ...
});
</script>
// vue2
<script>
export default {
mounted() { // 直接调用生命周期钩子
// ...
},
}
</script>
合并:beforeCreate+created=
setup
新增on:destroy->unmount
onErrorCaptured监听组件的统一报错:onErrorCaptured(error, component, details)
<template>
<div>
<p>{{ message }}</p>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello, Vue!'
};
},
methods: {
throwError() {
// 人为地抛出一个错误
throw new Error('An error occurred.');
}
},
onErrorCaptured(error, component, details) {
console.error('Captured an error in component:', component);
console.error('Error details:', error);
console.error('Details:', details);
// 可以选择返回 false 来阻止错误继续传播
// return false;
}
};
</script>
Fragment碎片组件:树
本质上 Vue3 每个组件还是一个根节点,因为 DOM 树只能是树状结构的,只是 Vue3 在编译阶段新增了判断,如果当前组件不只一个根元素,就添加一个 fragment
组件把这个多根组件的给包起来,相当于这个组件还是只有一个根节点。而 fragment
跟 keep-alive
一样是一个不会被渲染出来的内置组件
Vue2 不支持碎片
Vue3 支持碎片,可以拥有多个根节点
// vue2中在template里存在多个根节点会报错
<template>
<header></header>
<main></main>
<footer></footer>
</template>
// 只能存在一个根节点,需要用一个<div>来包裹着
<template>
<div>
<header></header>
<main></main>
<footer></footer>
</div>
</template>
非核心变化【但重要,可根据标题引导一波提问】
computed:有缓存(不必重算),依赖值发生变化触发
都支持函数和对象两种写法
vue2 函数
vue3 对象
watch:无缓存,单个监听值
vue2 属性(配置项:immediate,deep)
watch: {
// 监听 userId 的变化,触发 getData 方法
userId: 'getData',
// 监听 userName 的变化,触发 getData 方法
userName(newName, oldName) {
this.getData()
},
// 监听 userInfo 的变化,触发 getData 方法
userInfo: {
// 处理函数,在 userInfo 发生变化时触发
handler(newVal, oldVal) {
this.getData()
},
// 在组件创建时立即执行一次处理函数
immediate: true,
// 深度监听 userInfo 内部的属性变化
deep: true
}
}
vue3 函数(监听的属性,(newVal, oldVal) => { ... },配置项(+flush,debug))
<script setup>
import { watch, ref, reactive } from 'vue'
const name = ref('沐华')
const data = reactive({
age: 18,
money: 100000000000000000000,
children: []
})
// 监听 ref 属性
watch(name, (newName, oldName) => { ... })
// 监听其他属性、路由或者状态管理的都这样
watch(
() => data.age,
(newAge, oldAge) => { ... }
)
// 监听多个属性,数组放多个值,返回的新值和老值也是数组形式
watch([data.age, data.money], ([newAge, newMoney], [oldAge, oldMoney]) => { ... })
// 第三个参数是一个对象,为可配置项,有5个可配置属性
watch(data.children, (newList, oldList) => { ... }, {
// 这两个和 Vue2 一样,没啥说的
immediate: true,
deep: true,
// 回调函数的执行时机,默认在组件更新之前调用。更新后调用改成post
flush: 'pre', // 默认值是 pre,可改成 post 或 sync
// 下面两个是调试用的
onTrack (e) { debugger }
onTrigger (e) { debugger }
})
</script>
nextTick:DOM更新之后执行回调函数,返回一个Promise
vue2:作为vue的实例方法,this.$nextTick(()=>{})调用
// Vue2中的nextTick使用
this.$nextTick(() => {
// 在DOM更新后执行的操作
const element = document.getElementById('myElement')
console.log(element.getAttribute('data-value'))
})
vue3:独立的函数导入
<script setup>
import { nextTick} from 'vue'
// 方式 一
const handleClick = async () => {
await nextTick()
console.log('')
}
// 方式二
nextTick(() => {
console.log('')
})
// 方式三
nextTick().then(() => {
console.log('')
})
</script>
this
vue2:this=组件实例($router,$ref)
vue3:{ proxy, appContext } = getCurrentInstance()
<script setup>
import { getCurrentInstance } from 'vue'
// proxy 就是当前组件实例,可以理解为组件级别的 this,没有全局的、路由、状态管理之类的
const { proxy, appContext } = getCurrentInstance()
// 这个 global 就是全局实例
const global = appContext.config.globalProperties
</script>
组件传值
父子组件:子组件props来接受,$emit触发父组件的自定义事件;
兄弟组件:一个公共组件bus.js.。传递方通过事件触发bus.$emit
。接收方通过在mounted(){}生命周期里触发bus.$on
Vuex跨组件
Vue父子组件通信的缺点
-
逐层传递 props: 在多层次的组件嵌套中,如果需要在更深层的子组件中访问某些数据,就需要通过逐层传递 props,这会导致组件之间的耦合增加,并且在层级较深的情况下,维护起来可能会变得复杂。
-
Prop drilling(属性钻取): 如果父组件需要传递给孙子组件数据,但这些数据对于中间的子组件来说并不需要,那么这些数据会在组件层级之间进行无意义的传递,导致代码冗余和性能损耗。
-
难以追踪数据流: 在复杂的组件结构中,跟踪数据的传递和变化可能变得困难,特别是在大型应用中。这会增加调试和维护的难度。
-
无法直接修改 props: 从父组件传递给子组件的 prop 是只读的,子组件无法直接修改这些数据。如果需要在子组件中修改这些数据,需要通过触发事件或者使用 Vuex(状态管理模式)等方式来实现。
-
耦合性增加: 父子组件之间的通信可能导致它们之间紧密耦合,使得组件难以在其他上下文中重用,因为它们依赖于特定的数据传递方式。
为了解决这些问题,Vue 提供了其他更高级的组件通信机制,如:
-
Vuex(状态管理): 适用于管理大规模应用的共享状态,使得组件之间的数据共享和响应变得更加简单和可控。
-
事件总线: 可以通过 Vue 实例作为中央事件总线来实现组件之间的通信,尽管这也可能带来一些全局状态的问题。
-
Provide / Inject: 可以在父组件中使用
provide
,然后在子组件中使用inject
来实现跨多层次的组件通信,但也会增加组件之间的依赖关系。
选择合适的通信方式取决于应用的规模、复杂度以及团队的偏好,但在设计时需要权衡各种因素来避免可能的问题。
Vuex
熟悉⭐⭐
4.路由之间如何传参⭐⭐
通过router-link路由导航跳转传递
<router-link to=`/a/${id}`>routerlink传参</router-link>
1
跳转时使用push方法拼接携带参数。
this.$router.push({
path: `/getlist/${id}`,
})
1
2
3
通过路由属性中的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中的数据直接保存到浏览器缓存中。(一般是用sessionStorage)
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监测。
Vue 不能检测到对象属性的添加或删除。
异步更新队列:数据第一次的获取到了,也渲染了,但是第二次之后数据只有在再一次渲染页面的时候更新,并不能实时更新。
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条件渲染
18.Vue 中 for循环为什么加 key?⭐⭐
为了性能优化, 因为vue是虚拟DOM,更新DOM时用diff算法对节点进行一一比对,比如有很多li元素,要在某个位置插入一个li元素,但没有给li上加key,那么在进行运算的时候,就会将所有li元素重新渲染一遍,但是如果有key,那么它就会按照key一一比对li元素,只需要创建新的li元素,插入即可,不需要对其他元素进行修改和重新渲染。
key也不能是li元素的index,因为假设我们给数组前插入一个新元素,它的下标是0,那么和原来的第一个元素重复了,整个数组的key都发生了改变,这样就跟没有key的情况一样了。
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
参考:
前端面试题(附答案)完善中……_Jet_closer_burning的博客-CSDN博客
- 程序开发学习排行
-
- 1鸿蒙HarmonyOS:Web组件网页白屏检测
- 2HTTPS协议是安全传输,为啥还要再加密?
- 3HarmonyOS鸿蒙应用开发——数据持久化Preferences
- 4记解决MaterialButton背景颜色与设置值不同
- 5鸿蒙HarmonyOS实战-ArkUI组件(RelativeContainer)
- 6鸿蒙HarmonyOS实战-ArkUI组件(Stack)
- 7鸿蒙HarmonyOS实战-ArkUI组件(GridRow/GridCol)
- 8[Android][NDK][Cmake]一文搞懂Android项目中的Cmake
- 9鸿蒙HarmonyOS实战-ArkUI组件(mediaquery)
- 最近发表
-
- WooCommerce最好的WordPress常用插件下载博客插件模块的相关产品
- 羊驼机器人最好的WordPress常用插件下载博客插件模块
- IP信息记录器最好的WordPress常用插件下载博客插件模块
- Linkly for WooCommerce最好的WordPress常用插件下载博客插件模块
- 元素聚合器Forms最好的WordPress常用插件下载博客插件模块
- Promaker Chat 最好的WordPress通用插件下载 博客插件模块
- 自动更新发布日期最好的WordPress常用插件下载博客插件模块
- WordPress官方最好的获取回复WordPress常用插件下载博客插件模块
- Img to rss最好的wordpress常用插件下载博客插件模块
- WPMozo为Elementor最好的WordPress常用插件下载博客插件模块添加精简版