Vue3 面试题及详细答案120道 (76-90 )

前后端面试题》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,MySQL,Linux… 。

前后端面试题-专栏总目录

在这里插入图片描述

一、本文面试题目录

76. Vue3 中如何实现组件的按需加载且指定加载状态?

在 Vue3 中,可通过 defineAsyncComponent 实现组件的按需加载,并配合配置指定加载状态和错误处理。具体如下:

import { defineAsyncComponent } from 'vue';
import LoadingComponent from './LoadingComponent.vue';
import ErrorComponent from './ErrorComponent.vue';

// 按需加载组件,指定加载状态和错误状态
const AsyncComponent = defineAsyncComponent({
  // 加载组件的函数,返回 Promise
  loader: () => import('./TargetComponent.vue'),
  // 加载过程中显示的组件
  loadingComponent: LoadingComponent,
  // 加载失败时显示的组件
  errorComponent: ErrorComponent,
  // 延迟多久后显示加载组件(默认 200ms),避免短暂加载导致的闪烁
  delay: 300,
  // 超时时间,超过此时长视为加载失败(单位:ms)
  timeout: 5000
});

使用时,直接在模板中像普通组件一样使用 AsyncComponent 即可。这种方式能减少初始加载的资源体积,提升应用启动速度,同时通过加载状态提示优化用户体验。

77. 请解释 Vue3 中的 “响应式丢失” 问题及解决方案?

“响应式丢失” 指当对响应式对象进行解构、展开或赋值给新变量时,新变量可能失去响应式特性的问题。这是因为响应式对象的响应式依赖是基于属性访问的,解构等操作会破坏这种依赖关系。

示例

import { reactive } from 'vue';
const obj = reactive({ name: 'Vue3', age: 3 });

// 解构后,name 和 age 失去响应式
const { name, age } = obj;
name = 'Vue'; // 修改不会触发视图更新

// 展开到新对象,新对象非响应式
const newObj = { ...obj };
newObj.age = 4; // 修改不会触发视图更新

解决方案

  1. 使用 toRefs 解构:将响应式对象的属性转为 ref 对象,保持响应式。
    import { reactive, toRefs } from 'vue';
    const obj = reactive({ name: 'Vue3', age: 3 });
    const { name, age } = toRefs(obj); // name 和 age 是 ref 对象
    name.value = 'Vue'; // 会触发更新
    
  2. 直接访问原对象属性:避免解构,通过原响应式对象访问属性。
  3. 对新对象重新创建响应式:若需将展开的对象转为响应式,使用 reactiveref 重新包装。
    const newObj = reactive({ ...obj }); // newObj 是响应式对象
    

78. Vue3 中如何使用 keep-alive 缓存组件及控制缓存范围?

keep-alive 是 Vue 的内置组件,用于缓存不常变化的组件,避免重复渲染,提升性能。Vue3 中使用方式如下:

基本用法

<!-- 缓存包裹的组件 -->
<keep-alive>
  <ComponentA v-if="showA"></ComponentA>
  <ComponentB v-else></ComponentB>
</keep-alive>

控制缓存范围

  1. include:仅缓存名称匹配的组件(组件需通过 name 选项定义名称)。
    <!-- 仅缓存 ComponentA 和 ComponentB -->
    <keep-alive include="ComponentA,ComponentB">
      <router-view></router-view>
    </keep-alive>
    
  2. exclude:不缓存名称匹配的组件。
    <!-- 不缓存 ComponentC -->
    <keep-alive exclude="ComponentC">
      <router-view></router-view>
    </keep-alive>
    
  3. max:限制缓存组件的最大数量,超出时移除最早缓存的组件。
    <!-- 最多缓存 3 个组件 -->
    <keep-alive max="3">
      <router-view></router-view>
    </keep-alive>
    

配合路由缓存:通常用于 router-view,缓存路由组件,避免切换路由时重复加载。

79. 请比较 Vue3 中的 computedwatch 的使用场景?

computedwatch 都用于响应式数据的变化处理,但适用场景不同:

特性computedwatch
核心功能基于依赖数据计算衍生值,具有缓存监听数据变化,执行自定义逻辑
缓存机制依赖不变时,多次访问返回缓存结果无缓存,数据变化即执行回调
适用场景1. 从现有数据计算新值(如格式化数据)
2. 依赖多个数据的衍生值
3. 需要缓存计算结果的场景
1. 数据变化时执行异步操作(如接口请求)
2. 数据变化时执行复杂逻辑(如日志记录)
3. 需要获取新旧值对比的场景
写法函数式,返回计算结果需指定监听源和回调函数

示例

// computed:计算总价(依赖数量和单价)
const quantity = ref(2);
const price = ref(10);
const total = computed(() => quantity.value * price.value);

// watch:监听总价变化,发送请求
watch(total, (newTotal) => {
  api.updateTotal(newTotal); // 异步操作
});

80. 请描述 Vue3 中 setup 函数与 script setup 的区别?

setup 函数和 <script setup> 都是 Vue3 中使用 Composition API 的方式,但存在以下区别:

特性setup 函数<script setup>
写法位置在组件选项中定义(export default { setup() {} }作为 <script> 标签的属性(<script setup>
返回值处理需返回对象,对象属性才能在模板中使用无需返回,变量和函数直接在模板中可用
组件注册需在 components 选项中注册局部组件导入组件后自动注册,无需显式声明
Props/Emits通过参数 propscontext.emit 处理通过 definePropsdefineEmits 编译宏处理
代码简洁度相对繁琐,需手动返回和注册组件更简洁,减少模板代码,开发效率更高
类型支持需手动指定类型,类型推断较弱原生支持 TypeScript,类型推断更友好
生命周期使用需导入钩子函数并在 setup 中调用直接导入钩子函数调用,与代码逻辑融合更自然

示例

  • setup 函数:
    import { ref } from 'vue';
    import Child from './Child.vue';
    export default {
      components: { Child }, // 需注册组件
      setup(props) {
        const count = ref(0);
        return { count }; // 需返回才能在模板使用
      }
    };
    
  • <script setup>
    <script setup>
    import { ref } from 'vue';
    import Child from './Child.vue'; // 自动注册
    const count = ref(0); // 直接在模板中使用
    </script>
    

No.大剑师精品GIS教程推荐
0地图渲染基础- 【WebGL 教程】 - 【Canvas 教程】 - 【SVG 教程】
1Openlayers 【入门教程】 - 【源代码+示例 300+】
2Leaflet 【入门教程】 - 【源代码+图文示例 150+】
3MapboxGL【入门教程】 - 【源代码+图文示例150+】
4Cesium 【入门教程】 - 【源代码+综合教程 200+】
5threejs【中文API】 - 【源代码+图文示例200+】

81. Vue3 中如何实现组件的递归调用?

组件递归调用指组件在自身模板中调用自己,适用于树形结构、菜单等场景。Vue3 中实现方式如下:

  1. 定义组件并指定 name 选项(递归调用需依赖组件名称):

    <!-- Tree.vue -->
    <template>
      <div class="tree-node">
        <p>{{ node.label }}</p>
        <!-- 递归调用自身,渲染子节点 -->
        <Tree 
          v-for="child in node.children" 
          :key="child.id" 
          :node="child" 
          v-if="node.children && node.children.length"
        />
      </div>
    </template>
    
    <script>
    export default {
      name: 'Tree', // 必须指定 name,用于递归调用
      props: {
        node: {
          type: Object,
          required: true
        }
      }
    };
    </script>
    
  2. 使用组件

    <template>
      <Tree :node="rootNode" />
    </template>
    <script setup>
    import Tree from './Tree.vue';
    const rootNode = {
      label: '根节点',
      children: [
        { label: '子节点1', children: [{ label: '孙节点1' }] },
        { label: '子节点2' }
      ]
    };
    </script>
    

注意:需确保递归有终止条件(如 v-if 判断子节点是否存在),避免无限递归导致栈溢出。

82. 请解释 Vue3 中 markRawshallowRef/shallowReactive 的使用场景?

markRawshallowRefshallowReactive 是 Vue3 中用于控制响应式深度的 API,适用于特定性能优化场景:

  1. markRaw

    • 功能:标记一个对象为“非响应式”,使其永远不会被 Proxy 代理。
    • 场景:
      • 存储无需响应式的大型对象(如第三方库实例、图表配置)。
      • 避免对不可变数据进行响应式处理(节省性能)。
    • 示例:
      import { markRaw, reactive } from 'vue';
      const chart = markRaw(new Chart()); // 非响应式
      const obj = reactive({ chart }); // obj 是响应式,但 chart 仍为非响应式
      
  2. shallowRef

    • 功能:只对 .value 进行响应式处理,不递归处理内部对象。
    • 场景:
      • 存储大型对象,仅需监听对象引用变化,无需监听内部属性。
      • 优化性能,避免深层代理的开销。
    • 示例:
      import { shallowRef } from 'vue';
      const data = shallowRef({ list: [] });
      data.value.list.push(1); // 内部属性变化不触发更新
      data.value = { list: [1] }; // 引用变化触发更新
      
  3. shallowReactive

    • 功能:只对对象的第一层属性进行响应式处理,不递归代理嵌套对象。
    • 场景:
      • 处理结构固定的浅层对象(如表单的第一层字段)。
      • 已知嵌套对象无需响应式,减少代理开销。
    • 示例:
      import { shallowReactive } from 'vue';
      const obj = shallowReactive({ a: 1, b: { c: 2 } });
      obj.a = 2; // 第一层属性变化,触发更新
      obj.b.c = 3; // 嵌套属性变化,不触发更新
      

83. Vue3 中如何实现组件的强制更新?

在 Vue 中,响应式数据变化会自动触发组件更新,但某些特殊场景(如非响应式数据变化)可能需要强制更新组件。Vue3 中实现强制更新的方式如下:

  1. 使用 forceUpdate 方法

    • 通过 getCurrentInstance 获取组件实例,调用 forceUpdate 方法。
    import { getCurrentInstance } from 'vue';
    const instance = getCurrentInstance();
    
    // 强制更新
    const handleForceUpdate = () => {
      instance.ctx.$forceUpdate();
    };
    
  2. 通过 ref 触发更新

    • 定义一个无用的 ref 变量,修改其值触发更新(间接强制更新)。
    import { ref } from 'vue';
    const trigger = ref(0);
    
    // 强制更新
    const handleForceUpdate = () => {
      trigger.value++; // 修改 ref 值,触发组件更新
    };
    

注意:强制更新会跳过响应式依赖检查,直接触发组件重新渲染,可能导致性能问题,应尽量避免使用,优先通过规范的响应式数据驱动更新。

84. 请解释 Vue3 中的 v-bindv-model 的区别?

v-bindv-model 都是 Vue 中用于数据绑定的指令,但功能和使用场景不同:

特性v-bindv-model
绑定方向单向绑定(从数据到视图)双向绑定(数据到视图,视图到数据)
语法糖无,直接绑定属性值v-bind:value + v-on:input 的语法糖
使用场景绑定 HTML 属性、组件 props 等表单元素(input、select 等)、自定义组件
事件处理需手动绑定事件(如 @input)更新数据自动处理输入事件,同步数据
修饰符支持支持 .prop.camel 等属性修饰符支持 .trim.number 等表单修饰符

示例

  • v-bind 单向绑定:
    <!-- 绑定输入框 value(单向) -->
    <input v-bind:value="name" @input="name = $event.target.value" />
    
    <!-- 绑定组件 props -->
    <ChildComponent :msg="message" />
    
  • v-model 双向绑定:
    <!-- 表单双向绑定(等价于上面的 v-bind + @input) -->
    <input v-model="name" />
    
    <!-- 自定义组件双向绑定 -->
    <ChildComponent v-model="message" />
    

总结v-bind 适用于单向数据传递,v-model 适用于需要用户输入交互并同步数据的场景(如表单)。

85. Vue3 中如何处理组件的异步数据加载?

Vue3 中处理组件异步数据加载的常用方式有以下几种:

  1. onMounted 中加载

    • 组件挂载后发起请求,适用于简单场景。
    import { ref, onMounted } from 'vue';
    const data = ref(null);
    
    onMounted(async () => {
      data.value = await api.fetchData(); // 异步请求
    });
    
  2. 使用 watch 监听依赖加载

    • 当依赖数据(如路由参数)变化时重新加载数据。
    import { ref, watch } from 'vue';
    import { useRoute } from 'vue-router';
    const route = useRoute();
    const data = ref(null);
    
    // 监听路由参数变化
    watch(() => route.params.id, async (id) => {
      data.value = await api.fetchDataById(id);
    }, { immediate: true }); // 初始执行一次
    
  3. 结合 Suspense 和异步组件

    • 组件 setup 函数返回 Promise,配合 Suspense 显示加载状态。
    // AsyncDataComponent.vue
    <script setup>
    const data = await api.fetchData(); // setup 中使用 await
    </script>
    
    // 父组件使用
    <Suspense>
      <AsyncDataComponent />
      <template #fallback>加载中...</template>
    </Suspense>
    
  4. 使用组合式函数封装

    • 将异步逻辑封装为可复用函数,如 useFetch
    // useFetch.js
    import { ref, onMounted } from 'vue';
    export function useFetch(url) {
      const data = ref(null);
      const loading = ref(true);
      onMounted(async () => {
        data.value = await fetch(url).then(res => res.json());
        loading.value = false;
      });
      return { data, loading };
    }
    
    // 组件中使用
    <script setup>
    import { useFetch } from './useFetch';
    const { data, loading } = useFetch('/api/data');
    </script>
    

86. 请描述 Vue3 中自定义 hooks 的实现及使用场景?

自定义 hooks 是基于 Composition API 封装的可复用逻辑函数,命名通常以 use 开头,用于抽取组件中的共性逻辑,提升代码复用性。

实现方式

// useCounter.js(自定义 hook)
import { ref, computed, watch } from 'vue';

export function useCounter(initialValue = 0) {
  const count = ref(initialValue);
  const doubleCount = computed(() => count.value * 2);
  
  const increment = () => {
    count.value++;
  };
  
  const decrement = () => {
    count.value--;
  };
  
  // 监听 count 变化,可传入回调
  const watchCount = (callback) => {
    watch(count, callback);
  };
  
  return { count, doubleCount, increment, decrement, watchCount };
}

使用场景

  1. 通用逻辑复用:如计数器、表单处理、数据请求等。
    // 组件中使用
    <script setup>
    import { useCounter } from './useCounter';
    const { count, increment } = useCounter(10); // 初始值 10
    </script>
    
  2. 复杂逻辑拆分:将组件中的复杂逻辑拆分为多个 hooks,使代码结构清晰。
    // 组件中组合多个 hooks
    import { useFetch } from './useFetch';
    import { useForm } from './useForm';
    const { data } = useFetch('/api/data');
    const { form, validate } = useForm();
    
  3. 跨组件状态共享:结合 provide/inject,实现跨组件的逻辑复用。

优势:相比 mixins,自定义 hooks 不会导致命名冲突,逻辑更清晰,且类型支持更好。

87. Vue3 中如何实现路由参数的监听?

在 Vue3 中,可通过以下方式监听路由参数的变化(如动态路由 :id 的变化):

  1. 使用 watch 监听路由参数

    import { watch } from 'vue';
    import { useRoute } from 'vue-router';
    const route = useRoute();
    
    // 监听路由参数变化
    watch(
      () => route.params, // 监听 params 对象
      (newParams, oldParams) => {
        console.log('参数变化:', newParams.id);
        // 执行参数变化后的逻辑(如重新加载数据)
        loadData(newParams.id);
      },
      { immediate: true } // 初始加载时执行一次
    );
    
  2. 监听单个路由参数

    // 只监听 id 参数
    watch(
      () => route.params.id,
      (newId) => {
        console.log('id 变化:', newId);
        loadData(newId);
      },
      { immediate: true }
    );
    
  3. 使用组件内导航守卫 onBeforeRouteUpdate

    import { onBeforeRouteUpdate } from 'vue-router';
    
    onBeforeRouteUpdate((to, from) => {
      // 路由更新时触发(同一路由,参数变化)
      console.log('新参数:', to.params.id);
      loadData(to.params.id);
    });
    

注意:路由参数变化时,组件不会重新创建,因此 onMounted 钩子不会重新执行,需通过上述方式监听参数变化并处理逻辑。

88. 请解释 Vue3 中的 app.config.globalProperties 的作用及使用方式?

app.config.globalProperties 用于在 Vue3 应用中注册全局属性或方法,使所有组件都能访问这些属性或方法,类似 Vue2 中的 Vue.prototype

作用

  • 注册全局通用的工具函数、常量或第三方库实例(如 $api$utils)。
  • 避免在每个组件中重复导入,简化代码。

使用方式

  1. 注册全局属性

    // main.js
    import { createApp } from 'vue';
    import App from './App.vue';
    import api from './api'; // 接口工具
    
    const app = createApp(App);
    // 注册全局属性
    app.config.globalProperties.$api = api;
    app.config.globalProperties.$version = '1.0.0';
    
    app.mount('#app');
    
  2. 在组件中访问

    • 在模板中直接访问:
      <template>
        <div>{{ $version }}</div>
      </template>
      
    • <script setup> 中通过 getCurrentInstance 访问:
      import { getCurrentInstance } from 'vue';
      const instance = getCurrentInstance();
      const api = instance.appContext.config.globalProperties.$api;
      // 使用
      api.fetchData();
      

注意

  • 全局属性可能导致组件与全局依赖耦合,过度使用会降低组件可维护性。
  • 在 TypeScript 中,需扩展全局类型声明才能获得类型提示。

89. Vue3 中如何处理跨域请求?

Vue3 中处理跨域请求的方式与前端框架无关,主要通过以下两种方式解决:

  1. 开发环境:配置 Vite 代理
    vite.config.js 中配置代理,将前端请求转发到后端服务器,避免浏览器跨域限制。

    // vite.config.js
    import { defineConfig } from 'vite';
    import vue from '@vitejs/plugin-vue';
    
    export default defineConfig({
      plugins: [vue()],
      server: {
        proxy: {
          // 匹配以 /api 开头的请求
          '/api': {
            target: 'http://localhost:3000', // 后端接口地址
            changeOrigin: true, // 允许跨域
            rewrite: (path) => path.replace(/^\/api/, '') // 移除路径中的 /api
          }
        }
      }
    });
    

    前端请求示例:

    // 实际请求会被代理到 http://localhost:3000/data
    fetch('/api/data').then(res => res.json());
    
  2. 生产环境:后端配置 CORS
    后端服务器通过设置 Access-Control-Allow-* 响应头,允许跨域请求。

    // 后端(Node.js Express 示例)
    const express = require('express');
    const app = express();
    
    // 允许所有域名跨域(生产环境需指定具体域名)
    app.use((req, res, next) => {
      res.setHeader('Access-Control-Allow-Origin', '*');
      res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
      res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
      next();
    });
    

注意:开发环境代理仅用于本地调试,生产环境必须依赖后端 CORS 配置。

90. 请对比 Vue3 的 Composition API 与 React Hooks 的异同?

Vue3 的 Composition API 和 React Hooks 都是用于逻辑复用和组织的函数式 API,存在以下异同:

特性Composition APIReact Hooks
核心目的解决 Vue2 选项式 API 的逻辑复用问题解决 React class 组件的逻辑复用问题
依赖追踪自动依赖追踪(基于 Proxy 响应式系统)手动依赖数组(useEffect([deps])
生命周期映射onMounted 对应 componentDidMountuseEffect 统一处理生命周期逻辑
状态定义ref(基本类型)、reactive(对象)useState(所有类型,返回 [state, setter])
缓存机制computed 自动缓存计算结果useMemouseCallback 手动缓存
规则限制无严格调用顺序限制(可在条件中使用)必须在函数顶层调用,不能在条件中使用
代码复用自定义 hooks(useXXX 函数)自定义 hooks(useXXX 函数)
上下文使用provide/injectuseContext

示例

  • Composition API 实现计数器:
    import { ref, computed, onMounted } from 'vue';
    function useCounter() {
      const count = ref(0);
      const double = computed(() => count.value * 2);
      const increment = () => count.value++;
      onMounted(() => console.log('计数器初始化'));
      return { count, double, increment };
    }
    
  • React Hooks 实现计数器:
    import { useState, useMemo, useEffect } from 'react';
    function useCounter() {
      const [count, setCount] = useState(0);
      const double = useMemo(() => count * 2, [count]);
      const increment = () => setCount(c => c + 1);
      useEffect(() => {
        console.log('计数器初始化');
      }, []); // 空依赖模拟 componentDidMount
      return { count, double, increment };
    }
    

总结:两者核心思想一致(函数式逻辑复用),但实现细节因框架特性(响应式系统、渲染机制)不同而有差异。

二、120道面试题目录列表

文章序号vue3面试题120道
1vue3 面试题及详细答案(01 - 15)
2vue3 面试题及详细答案(16 - 30)
3vue3 面试题及详细答案(31 - 45)
4vue3 面试题及详细答案(46 - 60)
5vue3 面试题及详细答案(61 - 75)
6vue3 面试题及详细答案(76 - 90)
7vue3 面试题及详细答案(91 - 105)
8vue3 面试题及详细答案(106 - 120)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

还是大剑师兰特

打赏一杯可口可乐

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值