前端面试题 — — vue篇
这里是关于前端vue面试的一些题,我整理了一些经常被问到的问题,出现频率比较高的问题,以及个人经历过的问题。
前端面试笔记之vue篇
- 前言
-
- 1.数据双向绑定原理⭐⭐⭐
- 2.VUE3响应式比VUE2快的原因⭐
- 3. VUE生命周期⭐⭐⭐
- 4.组件之间如何传值⭐⭐⭐
- 5.v-model 的作用以及实现原理⭐⭐
- 6.谈一谈VUEX、pinia⭐
- 7.如何解决VUEX页面刷新数据丢失问题?⭐⭐
- 8.computed和watch的区别?⭐⭐⭐
- 9.如何封装axios?⭐
- 10.Route和router的区别⭐
- 11.v-show和v-if的区别⭐⭐⭐
- 12.vue中数据变了但是视图不跟新怎么解决?⭐
- 13.vue中data为什么是函数而不是对象?⭐⭐
- 14.VUE中父子组件传值,父组件异步请求,子组件不能实时更新怎么解决?(VUE中数据不能实时更新怎么解决?)⭐⭐⭐
- 15.父子组件传参emit如何传多个参数?⭐
- 16.VUE路由跳转方式⭐⭐
- 17.条件渲染v-if 与 v-for 优先级⭐
- 18.VUE 中 $nextTick 作用与原理?⭐⭐⭐
- 19.VUE中 for循环为什么加 key?⭐⭐⭐
- 20.VUE2和VUE3的区别?⭐⭐⭐
- 21.为什么VUE3不继续用$set?⭐⭐
- 22.VUE路由中的history和hash的区别⭐
- 23.copmuted、watch 在生命周期中执行的顺序⭐
- 24.Vue2与Vue3 Diff算法的区别⭐
- 25. 动态组件 & 异步组件⭐
- 26. 事件修饰符⭐
- 27.路由之间如何传参⭐
- 28.页面在编译时发生闪烁怎么解决?⭐
- 29. VUE2与VUE3 Tree shaking的区别⭐
- 30. VUE3中ref与reactive⭐⭐
- 31. 为什么 ref 可以替换整个对象?⭐
- 32. ref为什么要加.value?⭐
- 33. watch 和 watchEffect⭐
- 34. toRef 和 toRefs ⭐
- 35. vue中的性能优化⭐
- 其他
前言
这里是关于 vue面试的一些题,我整理了一些经常被问到的问题,出现频率比较高的问题,以及个人经历过的问题。如有不足之处,麻烦大家指出,持续更新中…(ps:一到三颗⭐代表重要性,⭐选择性了解,⭐⭐掌握,⭐⭐⭐前端需要知道的知识)
1.数据双向绑定原理⭐⭐⭐
vue2: 通过数据劫持结合发布—订阅(Dep-Watcher)模式,通过Object.defineProperty()为各个属性定义get、set方法,在数据发生改变时给订阅者发布消息,触发相应的事件回调。
vue3: 改用 Proxy 代理整个对象,通过 track(依赖收集) 和 trigger(触发更新) 实现响应式,解决了Vue 2的局限性,并优化了性能
2.VUE3响应式比VUE2快的原因⭐
-
Vue 3使用 Proxy实现响应式时,新增的属性和删除的属性会自动成为响应式的,无需开发者手动调用特定方法来实现响应式。
Vue 2使用 Object.defineProperty 实现响应式时,需要手动调用 Vue.set 来让新增属性变得响应式,否则新增属性不会触发视图更新。 -
Vue 3使用 Proxy 可以将整个对象视为响应式,当对象属性被读取或改变时,可以捕获所有这些操作,不需要像 Vue 2 那样为每个属性分别设置 getter 和 setter。
-
Vue 3使用 Proxy大大减少代码量,使代码结构更加清晰和简洁,提升可读性和可维护性。
-
Proxy 的底层实现针对响应式处理进行了进一步优化,能够更加高效地进行依赖追踪和更新。
Proxy底层实现原理主要基于JS的 Proxy API
- 使用 Proxy 对象可以对目标对象进行代理,通过传入一个处理器对象,该处理器对象中可以设置对目标对象的拦截操作,比如拦截读取、设置属性、删除属性等操作。
- 通过定义 Proxy 处理器对象的拦截器(handler),可以拦截目标对象上的各种操作。比如,可以在设置属性时触发侦听器,从而实现侦听属性的变化。
- 在 Proxy 的拦截器中常常会搭配使用 Reflect API,可以调用对应的 Reflect 方法来触发默认行为或者进行其他处理。
Proxy 接受两个参数:
target:要代理的目标对象。
handler:一个对象,定义了拦截操作(例如获取、设置属性等)的处理方法。
例:
const target = {};
const handler = {
get(target, prop:要读取的属性名(可以是字符串或 Symbol), receiver:从哪个对象当前属性被访问,通常用于继承链查找。) {
// 拦截器逻辑
return Reflect.get(target, prop, receiver);
},
set(target, prop, value, receiver) {
// 拦截器逻辑
return Reflect.set(target, prop, value, receiver);
}
};
const proxy = new Proxy(target, handler);
3. 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官网生命周期钩子
4.组件之间如何传值⭐⭐⭐
-
(2、3) Vue父子 组件之间传值
Props(父 → 子)(子 → 父) -
(2、3) 祖先和子孙传值
provide/inject
-
vue2兄弟组件之间的传值
bus
建一个公共组件bus.js.。传递方通过事件触发bus.$emit
。接收方通过在mounted(){}生命周期里触发bus.$on
。 -
vue2可以通过VUEX 来跨组件传参。
-
vue2父孙传值
$attrs
(向下)$listeners
(向上) -
vue2获取父组件实例
this.$parent
,获取子组件实例this.$refs
-
vue3 共享状态通过
Pinia
-
vue3 通过
v-model:modelValue
作为属性和update:modelValue
作为事件来传值 -
vue3 子组件通过
defineExpose
暴露方法与值 -
路由参数传值
-
插槽传值
详情可看vue之组件的传参方式
5.v-model 的作用以及实现原理⭐⭐
作用:v-model本质上不过是语法糖,默认对应组件的 value
属性 和 input
事件。
Vue 2的v-model本质是 value prop + input
事件的语法糖,但仅支持单绑定且存在属性冲突。
Vue 3 重构为 modelValue prop + update:modelValue
事件,并支持多v-model绑定(如v-model:title)和自定义修饰符。
实现过程:
vue2 实现:通过 value、input
vue3实现: 通过 modelValue和 update:modelValue
//vue2
<template>
<input @input="inputFn" :value="message">
</template>
<script setup>
import { ref, defineEmits } from "vue";
const message = ref("");
const emit = defineEmits(["message"]);
const inputFn = (e) => {
message.value = e.target.value;
emit("message", message.value);
};
</script>
父组件使用
<CustomInput @message="value => console.log(value)"/>
//vue3
<Child
:title="title"
@update:title="newVal => title = newVal"
:content="content"
@update:content="newVal => content = newVal"
/>
6.谈一谈VUEX、pinia⭐
原理:Vuex是专门为vue.js应用程序设计的状态管理工具。
构成:
- state:vuex的基本数据,用来存储变量,存放的数据是响应式的。
- mutations:提交更改数据,同步更新状态。
- actions:提交mutations,可异步操作。
- getters:是store的计算属性。
- modules:模块,每个模块里面有四个属性。
关于VUEX如何使用可以看VUE的传值问题
Vue3开始使用pinia
pinia与VUEX的区别
- mutations 不再存在
- 无需创建自定义复杂包装器来支持 TypeScript
- 不再需要注入、导入函数、调用函数、享受自动完成功能
- 无需动态添加 Store,默认情况下它们都是动态的
- 不再有 modules 的嵌套结构
- 没有 命名空间模块
参考Pinia 中文文档
7.如何解决VUEX页面刷新数据丢失问题?⭐⭐
原因:因为vuex里的数据是保存在运行内存中的,当页面刷新时,页面会重新加载vue实例,vuex里面的数据就会被清空。
解决方法:将vuex中的数据直接保存到浏览器缓存中。
另一种方法:使用插件vuex-persistedstate
。
vuex-persistedstate可以将Vuex store的状态持久化存储到浏览器的localStorage或sessionStorage中,使得用户下次打开页面时能够继续使用之前的应用状态。
8.computed和watch的区别?⭐⭐⭐
computed: 语法简洁更加简洁,自动追踪所有依赖,不支持异步,依赖不可变时自动缓存,初始化时自动计算 + 依赖变化时重新计算。
watch: 需编写回调函数,手动指定监听目标,支持异步, 无缓存,监听的数据变化时才触发回调。
computed应用场景:需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
watch应用场景:需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
详情请看 Vue的计算属性与methods、watch的区别
9.如何封装axios?⭐
这个根据自己项目实际情况来说。
列举:
- 安装 Axios
npm install axios
- 创建 Axios 实例
import axios from 'axios';
const http = axios.create({
baseURL: 'https:XXX.com', // 设置axios基础路径
timeout: 10000, // 设置请求超时
headers: {
'Content-Type': 'application/json',
},
});
export default http;
- 发送请求
GET、POST、PUT、DELETE 等
- 处理请求和响应拦截器
- 使用
可以参考下这个 axios 封装
10.Route和router的区别⭐
- route:是路由信息对象,包括“path,parms,hash,name“等路由信息参数。
- Router:是路由实例对象,包括了路由跳转方法,钩子函数等。
11.v-show和v-if的区别⭐⭐⭐
- v-if:真正的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;也是 惰性的,如果开始条件为false则什么都不做,只有为true才会编译。
- v-show:css切换,隐藏显示更适合频繁切换。在任何情况下都会被编译,然后被缓存,而且dom元素会被保留。 (v-show = false 则代表 display: none; true 则显示)
12.vue中数据变了但是视图不跟新怎么解决?⭐
原因(只在vue2中出现这种情况):
- 数组数据变动:使用某些方法操作数组,变动数据时,有些方法无法被vue监测,或者是数组长度的改变。
(concat(),split(),slice(),filter(),reduce())不会改变原始数组,而是返回一个新的数组,视图不会自动更新。 - Vue 不能检测到对象属性的添加或删除。
- 异步更新队列:数据第一次的获取到了,也渲染了,但是第二次之后数据只有在再一次渲染页面的时候更新,并不能实时更新。
解决方法:数组长度变化可以用splice来修改、需要监听某个属性的变化用$set、watch监听。
13.vue中data为什么是函数而不是对象?⭐⭐
官网中有这么一段介绍,详情可以看组件的复用
意思就是,在Vue中组件是可以被复用的,而当data是一个函数的时候,每一个实例的data都是独立的,不会相互影响了。
更详细的解释 ==>
14.VUE中父子组件传值,父组件异步请求,子组件不能实时更新怎么解决?(VUE中数据不能实时更新怎么解决?)⭐⭐⭐
首先了解父子组件生命周期执行顺序 ==>
vue2加载渲染数据过程
父组件 beforeCreate -->
父组件 created -->
父组件 beforeMount -->
子组件 beforeCreate -->
子组件 created -->
子组件 beforeMount -->
子组件 mounted -->
父组件 mounted -->
vue3加载渲染数据过程
父组件 onBeforeMount -->
子组件 onBeforeMount -->
子组件 onMounted -->
父组件 onMounted -->
原因:因为生命周期只会执行一次,数据是要等到异步请求以后才能拿到,那么子组件的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
}
})
}
<Child v-if="isTrue" :data="data" />
- 使用watch监听数据的变化
举例:
props: {
tableData: {
type: Array,
default: [],
},
},
watch: {
tableData(val){
console.log(val)
}
},
- 使用VueX
15.父子组件传参emit如何传多个参数?⭐
子组件:
submit(){
this.$emit('g',1,2,3,4,5)
}
父组件
g(val1,val2,val3,val4,val5) {
console.log(val1,val2,val3,val4,val5)
}
16.VUE路由跳转方式⭐⭐
- router-link 标签跳转
- this.$router.push()
- this.$router.replace() :不会留下历史记录
- this.$router.go(n) :(0:当前页,-1上一页,+1下一页,n代表整数)
- 使用 < router-view >
17.条件渲染v-if 与 v-for 优先级⭐
vue2.0文档是这么说的
vue2列表渲染指南
vue3.0文档是这么说的
vue3条件渲染
18.VUE 中 $nextTick 作用与原理?⭐⭐⭐
核心作用:
- 确保操作在 DOM 更新后执行
当数据变化后,Vue 并不会立即更新 DOM,而是将 DOM 更新操作推入异步队列。** nextTick** 允许在 DOM 更新完成后触发回调函数,解决了异步渲染获取不到更新后DOM。 - 解决异步渲染时序问题
修改数据后立刻需要获取最新的 DOM 元素属性(如宽度、焦点位置等)
Vue 在更新 DOM 时是异步执行的,在修改数据后,视图不会立刻更新,而是等同一事件循环中的所有数据变化完成之后,再统一进行视图更新。所以修改完数据,立即在方法中获取DOM,获取的仍然是未修改的DOM。
$nextTick 的原理: $nextTick本质是返回一个Promise 。
应用场景:
- 在created()里面想要获取操作Dom,把操作DOM的方法放在$nextTick中。
- 在data()中的修改后,页面中无法获取data修改后的数据,使用$nextTick时,当data中的数据修改后,可以实时的渲染页面
vue2官网中是这么说的
vue3官网中是这么说的
19.VUE中 for循环为什么加 key?⭐⭐⭐
作用
- 高效更新 DOM
通过唯一的 key,Vue 能识别列表元素身份,在数据变化时最小化 DOM 操作(避免不必要的节点销毁/重建)。 - 维持组件内部状态
确保可复用的元素(如含输入框的组件)在列表重排、过滤时保持正确的数据关联。
原理:通过新旧虚拟 DOM 对比(Diff)决定如何更新真实 DOM。
function updateChildren(oldCh, newCh) {
// 通过 key 生成旧节点 Map,如 { key1: node1, key2: node2 }
const oldKeyMap = createKeyMap(oldCh);
// 遍历新节点,查找可复用的旧节点
for (const newNode of newCh) {
const oldNode = oldKeyMap.get(newNode.key);
if (oldNode) {
patchVnode(oldNode, newNode); // 有 key 匹配,复旧节点用
} else {
createNewNode(newNode); // 无 key 匹配,新建
}
}
// 移除旧列表中的废弃节点...
}
实际场景示例
- 输入框内容错位
- 无 key:列表重排后,输入框内容留在原 DOM 位置
- 有 key:输入框随数据正确移动位置,内容正常
- 列表过滤或更新
- 无 key:若中间元素被删除,后续元素索引变化可能导致错误复用
- 有 key:精准定位变化元素,避免多余操作
官网是这样说的
详情可见官网内置的特殊 Attributes
20.VUE2和VUE3的区别?⭐⭐⭐
-
响应式
Vue 2: 使用的是基于 Object.defineProperty 的响应式系统。这种系统的响应式追踪机制对于对象的属性是有限的,动态添加属性时需要手动进行处理。
限制:无法响应数组元素的变化或对象的新增属性。Vue3 : 使用了新的响应式系统,基于 Proxy 实现。Proxy 使得 Vue 能够劫持对象的所有操作,从而实现更 细粒度的响应式追踪。
优势:可以更高效地处理属性的增删改查,对数组和对象的动态变化有更好的支持。
//Vue2
const data = {
count: 0
}
Object.defineProperty(data, 'count', {
get() {
return this.count
},
set(newValue) {
this.count = newValue
this.$emit('countChanged', newValue)
}
})
//Vue3
const data = reactive({
count: 0
})
-
性能优化
Vue3 引入了 虚拟 DOM 的优化和 编译时优化,显著提高了性能。
Tree-shaking:Vue 3 支持 tree-shaking,意味着在构建过程中可以去除未使用的代码,从而减小最终打包的体积。
更快的初始化和更新速度:相较于 Vue 2,Vue 3 的初始化和更新速度更快。 -
Composition API
Vue2:主要依赖于Options API
,即通过 data、methods、computed 等选项来定义组件的逻辑。
Vue3:引入了 Composition API,允许开发者在函数内部组织和复用逻辑,提供了更灵活的方式来组合组件逻辑和状态(setup
)。 -
TypeScript支持
Vue 2: TypeScript 支持有限.
Vue3: 更好的 TypeScript 支持,能够更好地与 TypeScript 进行集成,支持更强的类型推断和类型检查。 -
自定义指令
Vue 2: 自定义指令的使用和定义较为简单,但也有限。
Vue 3: 自定义指令的 API 更加灵活和强大,提供了更细粒度的控制。 -
Fragment 支持
Vue 2: 一个组件只能有一个根节点。要返回多个元素时,通常需要使用额外的包装元素(div
)。
Vue 3: 支持 Fragment,即组件可以返回多个根节点,不再需要额外的包装元素。这使得组件结构更加简洁。
//vue3
<template>
<div>部分内容</div>
<span>另一部分内容</span>
</template>
- 生命周期
Vue 2: 提供了如 created、mounted、updated、destroyed 等生命周期钩子函数。
Vue 3: 维持了大部分生命周期钩子,但在 Composition API 中,钩子的使用方式有所不同,比如 onMounted、onUnmounted 等。 - provide/inject
Vue 2: 使用方式较为基础。
Vue 3: 更加灵活,并支持组合式 API 中的使用。 - Teleport
vue3新增了Teleport
组件允许将子组件的内容渲染到 DOM 的任意位置。 - watchEffect、watchPostEffect
watchEffect
:用于响应式副作用,自动追踪依赖。watchPostEffect
:在 DOM 更新之后执行副作用。 - ref、reactive
Vue3 对 ref 和 reactive 的功能进行了改进,支持更多类型的数据,并提供了更好的类型推断。 - Suspense
Vue 3 引入了Suspense
组件,用于处理异步组件加载时的占位内容和错误处理。
vue3官网Suspense - v-if/v-for
Vue2 v-for优先级比v-if高
Vue3 v-if优先级比v-for高
21.为什么VUE3不继续用$set?⭐⭐
$set的作用:在vue2.0中:使用对象和数组来定义数据,当需要向对象或数组中新增一个属性或元素,并希望它在更新 View 时响应式地更新,就需要使用 $set方法来完成。
vue2是用object.definedProperty来实现数据响应的,无法监听深层数据的变化。
Vue3 中使用Proxy对数据代理通过ref和reactive将值和对象类型变为响应式对象,这样对它的修改和添加就能被vue捕获到,从而实现页面的自动刷新。
参考官网响应式基础
22.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
23.copmuted、watch 在生命周期中执行的顺序⭐
初始化阶段
beforeCreate–>data–>computed–>watch -->created
数据更新阶段
beforeUpdate–>computed–>watch–> updated
24.Vue2与Vue3 Diff算法的区别⭐
Vue2:采用 双端对比算法(首尾指针遍历),通过新旧节点的 start 和 end 指针逐步缩小范围,优先处理首尾相同节点(原地复用),其次是交叉对比(如旧头与新尾匹配时移动节点),最后通过 key 映射表查找可复用节点。
// updateChildren
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
if (sameVnode(oldStartVnode, newStartVnode)) {
patchVnode(oldStartVnode, newStartVnode);
oldStartIdx++;
newStartIdx++;
} else if (sameVnode(oldEndVnode, newEndVnode)) {
// 处理尾部匹配
} else if (sameVnode(oldStartVnode, newEndVnode)) {
// 交叉对比,移动节点
}
// ... 其他条件分支
}
Vue3:保留双端对比,但引入 动态标记(PatchFlag) 和 区块树,通过编译阶段标记动态内容(如文本、属性),跳过静态节点比对。
// patchChildren
if (patchFlag & PatchFlags.DYNAMIC_SLOTS) {
// 仅处理动态插槽
} else if (patchFlag & PatchFlags.TEXT) {
// 仅更新文本
}
Vue2: 依赖key的映射表查找可复用节点,需遍历新旧列表,时间复杂度为 O(n) 。
Vue3: 引入最长递增子序列(LIS)算法,在乱序列表中快速找到最小移动路径。例如,对动态列表的更新:
//LIS 实现
const lis = getSequence(newIndices);
for (let i = lis.length - 1; i >= 0; i--) {
// 仅移动必要节点
}
结合 key 和索引映射,减少 DOM 操作次数。
Vue3 通过编译时静态提升和动态标记(PatchFlag)缩小 Diff 范围,并采用最长递增子序列算法精准移动节点,而 Vue2 仅依赖双端对比全量递归比对子节点,效率更低。
25. 动态组件 & 异步组件⭐
动态组件和异步组件都是为了实现组件的动态渲染和按需加载,可以提高应用的性能和灵活性。
动态组件 :渲染组件时,根据某个条件动态地选择组件。
异步组件:组件的加载和渲染过程分成两部分进行,即先加载组件的代码和依赖,等加载成功后再将其渲染到页面上。这样可以避免在初始加载时一次性加载所有组件的代码和依赖,从而提高页面的性能和响应速度。在Vue中,可以使用工厂函数和组件的异步加载特性来实现异步组件的加载。
参考官网
vue2异步组件
vue3异步组件
26. 事件修饰符⭐
.stop:阻止单击事件继续传播
.prevent:提交事件不再重载页面
.capture :添加事件监听器时使用事件捕获模式
.self :当前元素自身时触发处理函数
.once :只会触发一次
.passive :提升移动端的性能。
其它:按键修饰符(.enter、.delete、.space、.esc、.up、.down等),系统修饰键(.ctrl、.alt),鼠标按钮修饰符(.left、.right)
<!-- 阻止单击事件继续传播 -->
<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>
详细请看官网事件修饰符
27.路由之间如何传参⭐
- 通过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在地址栏中不显示参数,刷新页面,参数丢失,
其余方法在地址栏中显示传递的参数,刷新页面,参数不丢失。
28.页面在编译时发生闪烁怎么解决?⭐
首先了解原因:
- 在页面加载时,Vue.js 组件可能会在数据就绪之前渲染。这可能会导致元素在数据可用之前短暂显示其初始状态。
- 使用直接在 DOM 中书写的模板时,可能会出现一种叫做“未编译模板闪现”的情况:用户可能先看到的是还没编译完成的双大括号标签,直到挂载的组件将它们替换为实际渲染的内容。
解决办法:使用指令v-cloak
v-cloak 会保留在所绑定的元素上,直到相关组件实例被挂载后才移除。 v-cloak用于隐藏尚未完成编译的 DOM 模板。
<template>
<div v-cloak>
<h1>{{ title }}</h1>
<p>{{ content }}</p>
</div>
</template>
<script>
export default {
data() {
return {
title: null,
content: null
}
},
created() {
// 从服务器获取数据并更新状态
this.fetchData();
},
methods: {
fetchData() {
// 模拟从服务器获取数据
setTimeout(() => {
this.title = '标题';
this.content = '内容';
}, 500);
}
}
}
</script>
在上面的示例中,v-cloak 用于隐藏元素,直到 fetchData 方法从服务器获取数据并更新状态。这将防止在数据加载之前闪烁效果。
相关可以看官网v-cloak
29. VUE2与VUE3 Tree shaking的区别⭐
首先了解tree shaking
是什么?
一种通过清除多余代码方式来优化项目打包体积的技术,专业术语叫 Dead code elimination
简单来讲,就是在保持代码运行结果不变的前提下,去除无用的代码
Vue 2 中的 Tree Shaking
在 Vue 2 中,由于 Vue 的核心库是以 CommonJS 模块的方式导出的,这导致了:
全量导入:当你在应用程序中使用 Vue 2 时,通常会使用像 import Vue from ‘vue’ 这样的语法来导入整个 Vue 核心。这种导入方式不利于 Tree Shaking,因为整个模块被引入,而不是按需导入和使用。
限制 Tree Shaking 效果:虽然 Vue 2 支持按需加载的写法(如使用 import { Component } from ‘vue’),但由于底层使用的是 CommonJS 格式,一些构建工具可能无法有效地进行 Tree Shaking。这意味着有些未使用的代码仍然可能被打包进最终的生产代码中。
Vue 3 中的 Tree Shaking
在 Vue 3 中,Vue 的核心库被重构为使用 ES Module 格式导出,这为 Tree Shaking 提供了更好的支持:
ES Module 导出:Vue 3 的核心库和许多内部功能模块都以 ES Module 形式导出,这种格式对于 Tree Shaking 是非常友好的。它允许构建工具在编译时更精确地识别和剔除未使用的代码。
更细粒度的导入:在 Vue 3 中,你可以使用更细粒度的导入语法,例如 import { createApp, defineComponent } from ‘vue’。这种方式使得构建工具可以更有效地识别并删除未被使用的模块和功能。
优化的体积:由于 Vue 3 的架构和导出方式的改进,应用程序在使用 Tree Shaking 时可以获得更小的包体积,只包含实际用到的代码。
总结:
在 Vue 2 中,由于使用了 CommonJS 格式的导出方式,Tree Shaking 的效果有限,很难完全消除未使用的代码,尤其是在全局导入 Vue 实例时。而在 Vue 3 中,采用了 ES Module 格式的导出,极大地增强了 Tree Shaking 的能力,使得构建工具能够更有效地剔除未使用的模块和代码片段,从而帮助优化应用程序的性能和体积。
30. VUE3中ref与reactive⭐⭐
ref:可以持有任何类型的值,包括深层嵌套的对象、数组或者 JavaScript 内置的数据结构,比如 Map
。
Ref 会使它的值具有深层响应性。这意味着即使改变嵌套对象或数组时,变化也会被检测到:
import { ref } from 'vue'
const obj = ref({
nested: { count: 0 },
arr: ['foo', 'bar']
})
function mutateDeeply () {
// 以下都会按照期望工作
obj.value.nested.count++
obj.value.arr.push('baz')
}
mutateDeeply()
reactive:
- 有限的值类型:它只能用于对象类型 (对象、数组和如 Map、Set 这样的
集合类型
)。它不能
持有如 string、number 或 boolean 这样的原始类型。 - 不能替换整个对象:
reactive
的Proxy代理绑定的是初始传入的对象。如果直接替换整个对象,新对象未被 Proxy 代理,响应性丢失
let state = reactive({ count: 0 })
//替换整个对象
state = reactive({ count: 1 }) //视图不更新
如果需要强制替换整个对象,改用 ref
- 对解构操作不友好:如果解构出基本类型的属性值,得到的会是一个普通变量,与原始响应式对象断开联系:
const state = reactive({ count: 0, user: { name: 'Alice' } });
// 直接访问响应式属性
console.log(state.count); // 响应式
// 解构基本类型属性:失去响应性
const { count } = state;
count++; // 视图不会更新!
// 解构对象属性:仍保留响应性(因为引用的是 Proxy 代理的对象)
const { user } = state;
user.name = 'Bob'; // 视图会更新
参考 :响应式基础
31. 为什么 ref 可以替换整个对象?⭐
ref 通过封装一个 .value 属性存储值,当替换 .value 时,Vue 能检测到整个引用的变化并触发更新。而 reactive 的 Proxy 直接绑定原对象,无法感知引用替换。
32. ref为什么要加.value?⭐
Proxy 只能代理对象,无法直接代理字符串、数字等原始值(但可通过包装对象实现)
- 支持基本类型
ref 允许创建对基本数据类型(如数字、字符串等)的响应式引用。为了能够将这些基本类型的值包装在对象中,Vue 需要一个方式来区分原始值和响应式包装。 - 对比与对象的响应式
在 Vue 3 中,使用 reactive 创建的响应式对象的属性是不需要额外的标记(像 .value)。这让开发者可以在访问对象属性时,直接使用它们。但是对于 ref,为了提供一致性和灵活性,Vue 选择使用 .value 这种语法。 - 可读性和一致性
使用 .value 可以清楚地区分出何时是访问普通对象属性,何时是访问响应式引用。这种明确的约定使得代码在某种程度上更易读。可以一目了然地区分对原始值的引用与对响应式引用的访问。 - 便于扩展
将响应式数据包装在对象中,也为未来的功能扩展提供了灵活性。
总结:为了支持基本类型的响应式引用、保持代码的一致性和可读性,同时也为将来的扩展提供了灵活性。
33. watch 和 watchEffect⭐
watch
- 用于监视特定数据的变化,并在数据变化时执行回调函数。
- 需要明确指定要监视的数据。
const count = ref(0);
count.value++
watch(count, (newValue, oldValue) => {
console.log(`count 从 ${oldValue} 变成了 ${newValue}`);
});
watchEffect
- 用于自动追踪其函数体内使用的响应式数据,并在这些数据变化时重新运行该函数。
- 声明一个响应式的副作用,只要函数中使用的响应式数据发生变化,就会执行函数体。
const isLoggedIn = ref(false);
watchEffect(() => {
if (isLoggedIn.value) {
console.log('用户已登录');
// 执行其他逻辑,例如更新用户信息
} else {
console.log('用户未登录');
// 执行其他逻辑,例如显示登录按钮
}
});
34. toRef 和 toRefs ⭐
toRef
- 将一个对象的特定属性转换为一个响应式引用(ref),这样可以独立地跟踪该属性的变化。
import { reactive, toRef } from 'vue';
const state = reactive({
count: 1,
name: 'Vue'
});
// 将 count 转换为一个 ref
const countRef = toRef(state, 'count');
// 现在可以独立地使用 countRef
countRef.value++; //2
toRefs
- 将整个响应式对象的所有属性都转换为响应式引用,返回一个包含这些属性的对象。
import { reactive, toRefs } from 'vue';
function useFeatureX() {
const state = reactive({
count: 1,
name: 'Vue'
});
return toRefs(state)
}
// 现在可以独立地使用 count 和 name
count.value++; //2
name.value = 'Vue 3'; //Vue 3
// 可以解构而不会失去响应性
const { count, name } = useFeatureX()
35. vue中的性能优化⭐
- 懒加载和按需加载
组件懒加载:使用 Vue 的异步组件特性,按需加载组件,而不是在应用启动时加载所有组件。
路由懒加载:在 Vue Router 中按需加载路由。
异步组件:使用 import() 来实现组件的按需加载,减少初始包的大小。 - 使用 Vuex 管理状态
- DOM 操作优化
使用 v-if 和 v-show:根据情境合理选择组件的渲染方式,避免无效的 DOM 操作。
合理使用 key:使用 key 帮助 Vue 跟踪节点,使得 Vue 的虚拟 DOM 更高效。 - 计算属性和方法
使用计算属性(computed与watch) - 组件缓存
对于需要频繁切换的组件,可以使用 keep-alive 来缓存组件,提高切换性能。 - 资源优化
图片优化与减少http请求 - 构建优化
代码拆分:通过动态导入和 Webpack 的优化特性实现代码拆分,减少初始加载体积。
Tree Shaking:在构建时移除未使用的代码,确保包体积最小化。 - 服务端渲染(SSR)
使用 Nuxt.js 等进行服务端渲染,以提升首屏加载速度和 SEO 性能。 - 监控与调试
使用 Vue Devtools 和性能分析工具,监测应用性能,找到瓶颈,进行有针对性的优化。
其他
关于前端其他面试题可以看这个
更多推荐
所有评论(0)