Vue 面试高频知识点详解
Vue 面试高频知识点详解
Vue 作为最流行的前端框架之一,是前端面试中的必考内容。本文整理了 Vue 面试中的高频考点,包括 Vue2 和 Vue3 的核心概念、原理和最佳实践。
一、Vue 核心概念
1. Vue 的响应式原理是什么?Vue2 和 Vue3 有什么区别?
Vue2 响应式原理:
Vue2 使用 Object.defineProperty 实现响应式系统。核心思想是通过遍历 data 选项中的所有属性,将每个属性转换为 getter/setter。当属性被访问时,触发 getter 进行依赖收集;当属性被修改时,触发 setter 派发更新。
// Vue2 响应式核心代码
function defineReactive(obj, key, val) {
const dep = new Dep();
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
// 依赖收集
if (Dep.target) {
dep.depend();
}
return val;
},
set(newVal) {
if (newVal === val) return;
val = newVal;
// 派发更新
dep.notify();
}
});
}Vue2 的局限性:
Vue3 响应式原理:
Vue3 使用 ES6 Proxy 实现响应式系统。Proxy 可以直接代理整个对象,支持数组索引检测、新增属性检测、删除属性检测等。
// Vue3 响应式核心代码
function reactive(target) {
return new Proxy(target, {
get(target, key, receiver) {
// 依赖收集
track(target, key);
const result = Reflect.get(target, key, receiver);
if (typeof result === 'object' && result !== null) {
return reactive(result);
}
return result;
},
set(target, key, value, receiver) {
const oldValue = target[key];
const result = Reflect.set(target, key, value, receiver);
if (oldValue !== value) {
trigger(target, key);
}
return result;
},
deleteProperty(target, key) {
const result = Reflect.deleteProperty(target, key);
trigger(target, key);
return result;
}
});
}Vue3 的优势:
2. Vue 的生命周期有哪些?各阶段适合做什么?
Vue2 生命周期:
Vue3 生命周期:
// Vue3 生命周期使用示例
import {
onBeforeMount, onMounted,
onBeforeUpdate, onUpdated,
onBeforeUnmount, onUnmounted
} from 'vue';
export default {
setup() {
onBeforeMount(() => {
console.log('挂载前');
});
onMounted(() => {
console.log('挂载后');
});
onBeforeUpdate(() => {
console.log('更新前');
});
onUpdated(() => {
console.log('更新后');
});
onBeforeUnmount(() => {
console.log('卸载前');
});
onUnmounted(() => {
console.log('卸载后');
});
}
};3. v-if 和 v-show 的区别是什么?
| 特性 | v-if | v-show |
|------|------|--------|
| 实现方式 | 真正的条件渲染,条件为 false 时元素不在 DOM 中 | 元素始终在 DOM 中,通过 display 控制显示/隐藏 |
| 初始渲染开销 | 低 | 高 |
| 切换开销 | 高 | 低 |
| 生命周期 | 会触发 | 不会触发 |
| 适用场景 | 不常切换 | 频繁切换 |
<template>
<div>
<!-- 频繁切换,使用 v-show -->
<div v-show="isVisible">频繁切换的内容</div>
<!-- 不常切换,使用 v-if -->
<div v-if="hasPermission">需要权限的内容</div>
</div>
</template>二、组件通信
4. Vue 组件通信有哪些方式?
1. props / emit(父子组件)
父组件通过 props 传递数据给子组件,子组件通过 emit 发送事件给父组件。
<!-- 父组件 -->
<template>
<ChildComponent :message="parentMessage" @child-event="handleEvent" />
</template>
<!-- 子组件 -->
<script>
export default {
props: ['message'],
methods: {
sendMessage() {
this.$emit('child-event', 'Hello from child');
}
}
};
</script>2. provide / inject(跨层级通信)
祖先组件通过 provide 提供数据,后代组件通过 inject 注入数据。
<!-- 祖先组件 -->
<script>
export default {
provide() {
return {
theme: this.theme,
updateTheme: this.updateTheme
};
},
data() {
return { theme: 'light' };
}
};
</script>
<!-- 后代组件 -->
<script>
export default {
inject: ['theme', 'updateTheme']
};
</script>3. Vuex / Pinia(全局状态管理)
// Pinia store
import { defineStore } from 'pinia';
export const useUserStore = defineStore('user', {
state: () => ({
user: null,
token: ''
}),
actions: {
login(userData) {
this.user = userData;
}
}
});
// 组件中使用
const userStore = useUserStore();
userStore.login(userData);5. props 和 data 有什么区别?
| 特性 | props | data |
|------|-------|------|
| 数据来源 | 父组件传递 | 组件内部定义 |
| 修改方式 | 不能直接修改 | 可以直接修改 |
| 用途 | 接收外部数据 | 管理内部状态 |
<script>
export default {
props: {
title: {
type: String,
required: true,
default: 'Default Title'
}
},
data() {
return {
internalCount: 0,
message: 'Hello'
};
}
};
</script>三、计算属性与监听器
6. computed 和 watch 的区别是什么?
| 特性 | computed | watch |
|------|----------|-------|
| 缓存 | 有缓存 | 无缓存 |
| 返回值 | 有返回值 | 无返回值 |
| 异步支持 | 不支持 | 支持 |
| 适用场景 | 派生状态 | 副作用处理 |
<script>
export default {
data() {
return {
firstName: 'John',
lastName: 'Doe'
};
},
computed: {
// 计算属性:有缓存
fullName() {
return this.firstName + ' ' + this.lastName;
}
},
watch: {
// watch:无缓存,支持异步
firstName(newVal, oldVal) {
console.log('watch called');
this.fetchData(newVal);
}
},
methods: {
async fetchData(name) {
const res = await fetch('/api/data?name=' + name);
return await res.json();
}
}
};
</script>四、Vue3 新特性
7. Composition API 和 Options API 有什么区别?
Options API 的缺点:
Composition API 的优势:
<!-- Options API -->
<script>
export default {
data() {
return {
count: 0,
user: null
};
},
methods: {
increment() {
this.count++;
}
},
mounted() {
this.fetchUser();
}
};
</script>
<!-- Composition API -->
<script setup>
import { ref, onMounted } from 'vue';
const count = ref(0);
const user = ref(null);
const increment = () => {
count.value++;
};
const fetchUser = async () => {
// 获取用户
};
onMounted(() => {
fetchUser();
});
</script>8. ref 和 reactive 有什么区别?
| 特性 | ref | reactive |
|------|-----|----------|
| 适用类型 | 基本类型和对象 | 仅对象和数组 |
| 访问方式 | 通过 .value | 直接访问属性 |
| 模板中 | 自动解包 | 不解包 |
| 替换 | 可以替换 | 不能替换 |
import { ref, reactive } from 'vue';
// ref:适合基本类型
const count = ref(0);
count.value++;
// reactive:适合对象
const state = reactive({
count: 0,
user: { name: 'Alice' }
});
state.count++;
state.user.name = 'Bob';五、性能优化
9. Vue 性能优化有哪些手段?
1. 渲染优化
2. 组件优化
3. 响应式优化
4. 打包优化
<template>
<!-- 使用唯一 key -->
<div v-for="item in items" :key="item.id">{{ item.name }}</div>
<!-- 缓存组件 -->
<keep-alive>
<component :is="currentComponent" />
</keep-alive>
</template>
<script setup>
import { shallowRef } from 'vue';
// 冻结大列表
const largeList = shallowRef([]);
</script>六、Vue Router
10. Vue Router 有哪些导航守卫?
全局守卫:
路由独享守卫:
组件内守卫:
// 全局前置守卫
router.beforeEach((to, from, next) => {
const token = localStorage.getItem('token');
if (to.meta.requiresAuth && !token) {
next('/login');
} else {
next();
}
});
// 组件内守卫
export default {
beforeRouteLeave(to, from, next) {
const answer = window.confirm('确定要离开吗?');
next(answer);
}
};七、Vuex 与 Pinia
11. Vuex 和 Pinia 有什么区别?
| 特性 | Vuex | Pinia |
|------|------|-------|
| 官方推荐 | Vue2 | Vue3 |
| mutations | 需要 | 不需要 |
| TypeScript | 支持一般 | 支持优秀 |
| 体积 | 较大 | 更小 |
// Vuex
import { createStore } from 'vuex';
export default createStore({
state: { count: 0 },
mutations: {
increment(state) {
state.count++;
}
}
});
// Pinia
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
actions: {
increment() {
this.count++;
}
}
});