面试题答案
一键面试架构设计
- 全局错误捕获:在 Vue 应用的入口处使用
app.config.errorHandler
来捕获全局未处理的错误。这可以确保无论错误发生在哪个组件或异步操作中,都能被统一处理。例如:
import { createApp } from 'vue';
import App from './App.vue';
const app = createApp(App);
app.config.errorHandler = (err, vm, info) => {
// 记录错误信息,可发送到日志服务器
console.error(`Error in ${info}: ${err.message}`, err);
// 可以弹出提示框告知用户发生错误
};
app.mount('#app');
- 组件内错误处理:在每个异步组件内部,使用
try - catch
块来捕获可能发生的错误。对于异步操作,如fetch
请求,可以这样处理:
<template>
<div>
<!-- 组件内容 -->
</div>
</template>
<script setup>
import { ref } from 'vue';
const data = ref(null);
const error = ref(null);
try {
const response = await fetch('/api/data');
if (!response.ok) {
throw new Error('Network response was not ok');
}
data.value = await response.json();
} catch (e) {
error.value = e;
}
</script>
- 加载状态管理:使用 Vuex 或 Pinia 来管理全局的加载状态。创建一个
loading
状态,在异步操作开始时将其设置为true
,结束时设置为false
。例如,在 Pinia 中:
import { defineStore } from 'pinia';
export const useLoadingStore = defineStore('loading', {
state: () => ({
isLoading: false
}),
actions: {
startLoading() {
this.isLoading = true;
},
stopLoading() {
this.isLoading = false;
}
}
});
然后在组件中调用这些 actions:
<template>
<div>
<button @click="fetchData">Fetch Data</button>
<div v-if="loading.isLoading">Loading...</div>
</div>
</template>
<script setup>
import { useLoadingStore } from '@/stores/loading';
import { ref } from 'vue';
const loading = useLoadingStore();
const data = ref(null);
const fetchData = async () => {
loading.startLoading();
try {
const response = await fetch('/api/data');
data.value = await response.json();
} catch (e) {
console.error(e);
} finally {
loading.stopLoading();
}
};
</script>
代码组织
- 错误处理函数封装:将错误处理逻辑封装成独立的函数,以便在多个组件中复用。例如:
const handleError = (error) => {
// 记录错误
console.error(error);
// 显示用户友好的错误提示
alert('An error occurred. Please try again later.');
};
然后在组件中调用:
<template>
<div>
<!-- 组件内容 -->
</div>
</template>
<script setup>
import { handleError } from '@/utils/error';
try {
// 异步操作
} catch (e) {
handleError(e);
}
</script>
- 异步组件模块化:将不同模块的异步组件分开存放,并按照功能进行分类。例如,用户相关的异步组件放在
src/components/user
目录下,订单相关的放在src/components/order
目录下。每个组件的加载逻辑和错误处理逻辑都在其内部进行封装,保持组件的独立性。
性能优化
- 代码分割与懒加载:对于大型应用,使用动态导入(
import()
)进行代码分割和异步组件的懒加载。这样可以减少初始加载的代码量,提高应用的加载速度。例如:
<template>
<div>
<component :is="asyncComponent"></component>
</div>
</template>
<script setup>
import { ref } from 'vue';
const asyncComponent = ref(null);
const loadComponent = async () => {
try {
asyncComponent.value = await import('./SomeAsyncComponent.vue');
} catch (e) {
console.error(e);
}
};
loadComponent();
</script>
- 缓存机制:对于频繁加载且数据不经常变化的异步组件,可以添加缓存机制。在 Vuex 或 Pinia 中设置一个缓存对象,当组件加载时先检查缓存中是否有数据,如果有则直接使用,避免重复请求。例如:
import { defineStore } from 'pinia';
export const useDataStore = defineStore('data', {
state: () => ({
dataCache: {}
}),
actions: {
async fetchData(key) {
if (this.dataCache[key]) {
return this.dataCache[key];
}
try {
const response = await fetch(`/api/data/${key}`);
const data = await response.json();
this.dataCache[key] = data;
return data;
} catch (e) {
console.error(e);
return null;
}
}
}
});
实际案例
假设我们正在开发一个电商管理系统,其中有订单管理模块和商品管理模块。订单列表组件和商品详情组件都是异步加载的。
- 架构方面:在应用入口使用
app.config.errorHandler
捕获全局错误,确保系统在任何组件出错时都能有统一的处理。在订单列表组件内部,使用try - catch
处理获取订单数据的fetch
请求错误,并通过 Pinia 管理加载状态。 - 代码组织:将订单相关的组件放在
src/components/order
目录下,每个组件有自己独立的加载和错误处理逻辑。同时,将错误处理函数封装在@/utils/error.js
中供多个组件复用。 - 性能优化:订单列表组件和商品详情组件都使用动态导入进行懒加载,减少初始加载时间。对于商品数据,因为不经常变化,使用缓存机制在 Pinia 中缓存,避免重复获取。