Vue3 面试题及详细答案120道(61-75 )

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

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

文章目录

  • 一、本文面试题目录
      • [61. Vue3 中如何实现组件的异步加载?](#61. Vue3 中如何实现组件的异步加载?)
      • [62. 什么是 Vue3 的组合式 API 中的"依赖收集"?](#62. 什么是 Vue3 的组合式 API 中的“依赖收集”?)
      • [63. Vue3 中如何使用 Teleport 组件?](#63. Vue3 中如何使用 Teleport 组件?)
      • [64. Vue3 与 Vue2 的生命周期钩子有哪些区别?](#64. Vue3 与 Vue2 的生命周期钩子有哪些区别?)
      • [65. Vue3 中 `v-model` 的实现原理是什么?](#65. Vue3 中 v-model 的实现原理是什么?)
      • [66. Vue3 中的 `ref` 和 `reactive` 有什么性能差异?](#66. Vue3 中的 refreactive 有什么性能差异?)
      • [67. 如何在 Vue3 中实现全局状态管理(不使用 Vuex/Pinia)?](#67. 如何在 Vue3 中实现全局状态管理(不使用 Vuex/Pinia)?)
      • [68. Vue3 中 `watch` 和 `watchEffect` 的区别是什么?](#68. Vue3 中 watchwatchEffect 的区别是什么?)
      • [69. Vue3 中如何处理组件的错误边界?](#69. Vue3 中如何处理组件的错误边界?)
      • [70. 什么是 Vue3 的"渲染函数"?如何使用?](#70. 什么是 Vue3 的“渲染函数”?如何使用?)
      • [71. Vue3 中如何实现跨组件通信?](#71. Vue3 中如何实现跨组件通信?)
      • [72. Vue3 中的 `defineProps` 和 `defineEmits` 有什么作用?](#72. Vue3 中的 definePropsdefineEmits 有什么作用?)
      • [73. Vue3 中如何优化长列表渲染性能?](#73. Vue3 中如何优化长列表渲染性能?)
      • [74. 什么是 Vue3 的"编译器宏"?有哪些常用宏?](#74. 什么是 Vue3 的“编译器宏”?有哪些常用宏?)
      • [75. Vue3 中如何实现路由守卫?](#75. Vue3 中如何实现路由守卫?)

一、本文面试题目录

61. Vue3 中如何实现组件的异步加载?

在 Vue3 中,可通过 defineAsyncComponent 函数实现组件的异步加载,它能将组件加载推迟到需要时进行,提升初始加载速度。

  • 基本用法:

    javascript 复制代码
    import { defineAsyncComponent } from 'vue'
    // 异步加载组件
    const AsyncComponent = defineAsyncComponent(() => import('./AsyncComponent.vue'))
  • 高级配置(可指定加载状态、错误处理等):

    javascript 复制代码
    const AsyncComponent = defineAsyncComponent({
      loader: () => import('./AsyncComponent.vue'),
      loadingComponent: LoadingComponent, // 加载中显示的组件
      errorComponent: ErrorComponent, // 加载失败显示的组件
      delay: 200, // 延迟多久后显示加载组件(默认 200ms)
      timeout: 3000 // 超时时间,超过则显示错误组件
    })

62. 什么是 Vue3 的组合式 API 中的"依赖收集"?

依赖收集是 Vue3 响应式系统的核心机制,指追踪组件渲染过程中使用的响应式数据,当这些数据变化时,自动触发相关组件或副作用的重新执行。

  • 实现原理:
    1. 执行副作用函数(如组件渲染函数、watch 回调)时,Vue 会将当前副作用函数设为"活跃状态"。
    2. 访问响应式数据时,数据的 getter 会将活跃副作用函数添加到自身的依赖列表中。
    3. 当数据变化触发 setter 时,会遍历依赖列表,执行所有关联的副作用函数。

63. Vue3 中如何使用 Teleport 组件?

Teleport( teleport 译为"传送")用于将组件的 DOM 结构"传送"到页面的指定位置,解决嵌套组件样式或层级冲突问题(如模态框、弹窗)。

  • 基本用法:

    vue 复制代码
    <template>
      <button @click="showModal = true">打开弹窗</button>
      <Teleport to="body"> <!-- 将内容传送到 body 标签下 -->
        <div v-if="showModal" class="modal">
          <p>这是弹窗内容</p>
          <button @click="showModal = false">关闭</button>
        </div>
      </Teleport>
    </template>
    <script setup>
    import { ref } from 'vue'
    const showModal = ref(false)
    </script>
  • 注意:to 属性值可以是 CSS 选择器或 DOM 元素,且传送的内容仍受当前组件的响应式数据控制。

64. Vue3 与 Vue2 的生命周期钩子有哪些区别?

Vue3 保留了大部分 Vue2 的生命周期概念,但在组合式 API 中采用函数式写法,且部分钩子名称有调整:

Vue2 选项式 API Vue3 组合式 API 说明
beforeCreate 无(可在 setup 中替代) 初始化前,setup 执行时机类似
created 无(可在 setup 中替代) 初始化后,setup 执行时机类似
beforeMount onBeforeMount 挂载前触发
mounted onMounted 挂载后触发
beforeUpdate onBeforeUpdate 更新前触发
updated onUpdated 更新后触发
beforeDestroy onBeforeUnmount 卸载前触发(名称调整)
destroyed onUnmounted 卸载后触发(名称调整)
errorCaptured onErrorCaptured 捕获子组件错误时触发
  • 新增钩子:onRenderTracked(渲染追踪时触发)、onRenderTriggered(渲染触发时触发),用于调试响应式依赖。

65. Vue3 中 v-model 的实现原理是什么?

Vue3 中 v-model 是语法糖,本质是通过props 和事件 实现父子组件数据双向绑定,相比 Vue2 更灵活,支持自定义修饰符和多个 v-model

  • 基本原理:

    • 父组件使用 v-model:propName="value" 时,等价于 :propName="value" @update:propName="value = $event"
    • 子组件通过 defineProps 接收 propName,并通过 defineEmits 触发 update:propName 事件传递新值。
  • 示例:

    vue 复制代码
    <!-- 父组件 -->
    <ChildComponent v-model:count="num" />
    
    <!-- 子组件 -->
    <template>
      <button @click="handleClick">点击</button>
    </template>
    <script setup>
    const props = defineProps(['count'])
    const emit = defineEmits(['update:count'])
    const handleClick = () => {
      emit('update:count', props.count + 1) // 触发更新事件
    }
    </script>

66. Vue3 中的 refreactive 有什么性能差异?

refreactive 都是创建响应式数据的 API,但实现机制不同,性能表现有差异:

  • reactive
    • 基于 Proxy 实现,直接代理对象,对对象的属性访问/修改进行拦截。
    • 性能:对于大型对象,初始化时需要递归代理所有属性,可能有轻微性能损耗;但访问属性时无需额外解包,效率较高。
  • ref
    • 用于基本类型或单个值,内部通过 { value: ... } 包装,访问/修改需通过 .value
    • 性能:初始化轻量,但每次访问/修改都需经过 .value 的 getter/setter,频繁操作时可能比 reactive 略慢。
  • 建议:
    • 基本类型或单个值用 ref,对象/数组用 reactive
    • 频繁操作的响应式数据优先考虑 reactive(如复杂表单)。

67. 如何在 Vue3 中实现全局状态管理(不使用 Vuex/Pinia)?

可通过响应式数据 + 全局注入实现简单全局状态管理,适用于小型项目:

  1. 创建全局响应式数据:

    javascript 复制代码
    // store.js
    import { reactive, readonly } from 'vue'
    const state = reactive({
      userInfo: null,
      theme: 'light'
    })
    // 对外暴露只读版本,防止直接修改
    const globalState = readonly(state)
    // 定义修改状态的方法
    const mutations = {
      setUserInfo(info) {
        state.userInfo = info
      },
      toggleTheme() {
        state.theme = state.theme === 'light' ? 'dark' : 'light'
      }
    }
    export { globalState, mutations }
  2. 在组件中使用:

    vue 复制代码
    <script setup>
    import { globalState, mutations } from './store.js'
    // 读取状态
    console.log(globalState.theme)
    // 修改状态
    mutations.setUserInfo({ name: 'Vue3' })
    </script>
  • 缺点:缺乏 Vuex/Pinia 的模块化、DevTools 追踪、中间件等功能,大型项目仍建议使用 Pinia。

68. Vue3 中 watchwatchEffect 的区别是什么?

watchwatchEffect 都是监听响应式数据变化的 API,但用法和场景不同:

特性 watch watchEffect
依赖指定 需明确指定监听的数据源(如 watch(refA, ...) 自动收集依赖(函数内使用的响应式数据均会被监听)
初始执行 默认不执行,需通过 immediate: true 开启 默认初始执行一次
回调参数 可获取新旧值((newVal, oldVal) => {} 无参数,仅执行副作用
适用场景 需明确监听特定数据,或需要新旧值对比 依赖不明确,或需要初始执行的副作用(如数据加载)
  • 示例:

    javascript 复制代码
    const count = ref(0)
    // watch:明确监听 count
    watch(count, (newVal, oldVal) => {
      console.log('count变化:', newVal, oldVal)
    })
    
    // watchEffect:自动监听内部使用的 count
    watchEffect(() => {
      console.log('count当前值:', count.value)
    })

69. Vue3 中如何处理组件的错误边界?

Vue3 中可通过 onErrorCaptured 钩子或自定义错误边界组件捕获子组件的错误,防止错误扩散导致整个应用崩溃:

  • 方法1:使用 onErrorCaptured 钩子(组件内局部捕获):

    vue 复制代码
    <script setup>
    import { onErrorCaptured, ref } from 'vue'
    const hasError = ref(false)
    
    onErrorCaptured((err, instance, info) => {
      console.error('捕获到错误:', err, info)
      hasError.value = true
      return true // 阻止错误继续向上传播
    })
    </script>
  • 方法2:自定义错误边界组件(全局复用):

    vue 复制代码
    <!-- ErrorBoundary.vue -->
    <template>
      <slot v-if="!hasError" />
      <div v-else>发生错误:{{ error.message }}</div>
    </template>
    <script setup>
    import { ref, onErrorCaptured } from 'vue'
    const hasError = ref(false)
    const error = ref(null)
    
    onErrorCaptured((err) => {
      hasError.value = true
      error.value = err
      return true
    })
    </script>
  • 使用错误边界组件:

    vue 复制代码
    <ErrorBoundary>
      <ChildComponent /> <!-- 子组件的错误会被捕获 -->
    </ErrorBoundary>

70. 什么是 Vue3 的"渲染函数"?如何使用?

渲染函数是用 JavaScript 代码描述组件渲染内容的函数,相比模板更灵活,适用于动态生成复杂 DOM 结构的场景。Vue3 中通过 h 函数(createVNode 的别名)创建虚拟 DOM 节点。

  • 基本用法:

    vue 复制代码
    <script setup>
    import { h } from 'vue'
    
    // 定义渲染函数组件
    const MyComponent = (props) => {
      return h('div', { class: 'my-component' }, [
        h('h1', `Hello ${props.name}`),
        h('p', '这是渲染函数生成的内容')
      ])
    }
    
    MyComponent.props = ['name'] // 声明 props
    </script>
    
    <template>
      <MyComponent name="Vue3" />
    </template>
  • 与模板的关系:模板最终会被编译为渲染函数,渲染函数是 Vue 内部渲染的底层实现。


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

71. Vue3 中如何实现跨组件通信?

Vue3 提供多种跨组件通信方式,适用于不同场景:

  1. Props / Emits :父子组件通信,父传子用 props,子传父用 emits

  2. Provide / Inject :祖孙组件通信,祖先通过 provide 提供数据,后代通过 inject 接收。

    javascript 复制代码
    // 祖先组件
    import { provide } from 'vue'
    provide('theme', 'dark')
    
    // 后代组件
    import { inject } from 'vue'
    const theme = inject('theme', 'light') // 第二个参数为默认值
  3. 全局状态管理:如 Pinia、Vuex,适用于任意组件间共享状态。

  4. 事件总线(Event Bus) :通过 mitt 库实现(Vue3 移除了内置 $on/$emit):

    javascript 复制代码
    // bus.js
    import mitt from 'mitt'
    export const bus = mitt()
    
    // 组件A发送事件
    bus.emit('user-updated', userInfo)
    
    // 组件B接收事件
    bus.on('user-updated', (info) => { ... })

72. Vue3 中的 definePropsdefineEmits 有什么作用?

definePropsdefineEmits 是 Vue3 <script setup> 语法中用于声明组件 props 和自定义事件的编译宏,无需导入即可使用:

  • defineProps:声明组件接收的 props,支持类型校验和默认值。

    javascript 复制代码
    // 基础用法
    const props = defineProps(['name', 'age'])
    
    // 带类型和默认值
    const props = defineProps({
      name: { type: String, required: true },
      age: { type: Number, default: 18 }
    })
  • defineEmits:声明组件可触发的自定义事件,支持类型校验。

    javascript 复制代码
    // 基础用法
    const emit = defineEmits(['change', 'submit'])
    
    // 带类型校验
    const emit = defineEmits({
      change: (value) => typeof value === 'string', // 验证事件参数
      submit: () => true // 无需参数
    })
    
    // 触发事件
    emit('change', 'new value')
  • 特点:在 <script setup> 中自动具备类型推断,且返回的 props 是响应式的(只读),emit 是触发事件的函数。

73. Vue3 中如何优化长列表渲染性能?

长列表(如 1000+ 条数据)渲染可能导致性能问题,可通过以下方式优化:

  1. 虚拟滚动 :只渲染可视区域内的列表项,通过 vue-virtual-scroller 等库实现。

    vue 复制代码
    <template>
      <RecycleScroller
        :items="longList"
        :item-size="50"
        class="scroller"
      >
        <template v-slot="{ item }">
          <div class="list-item">{{ item.text }}</div>
        </template>
      </RecycleScroller>
    </template>
  2. 懒加载:监听滚动事件,当列表滚动到一定位置时再加载更多数据。

  3. 避免响应式数据冗余 :长列表数据若无需响应式,可使用 markRaw 标记为非响应式。

    javascript 复制代码
    import { markRaw } from 'vue'
    const longList = markRaw([/* 大量数据 */]) // 关闭响应式
  4. 使用 v-memo :缓存列表项,仅当依赖变化时重新渲染。

    vue 复制代码
    <div v-for="item in list" :key="item.id" v-memo="[item.id, item.name]">
      {{ item.name }}
    </div>

74. 什么是 Vue3 的"编译器宏"?有哪些常用宏?

编译器宏是 Vue3 中仅在 <script setup>setup() 函数中生效的特殊函数,由 Vue 编译器处理,无需导入即可使用,用于简化组件逻辑:

  • 常用编译器宏:
    1. defineProps:声明组件 props。

    2. defineEmits:声明组件自定义事件。

    3. defineExpose:暴露组件内部属性/方法,供父组件通过 ref 访问。

      javascript 复制代码
      const count = ref(0)
      const increment = () => count.value++
      defineExpose({ count, increment }) // 父组件可通过 ref 访问
    4. withDefaults:为 defineProps 提供更灵活的默认值配置(支持函数返回默认值)。

      javascript 复制代码
      const props = withDefaults(defineProps({
        list: { type: Array },
        config: { type: Object }
      }), {
        list: () => [], // 函数返回默认值(避免复用同一引用)
        config: () => ({ theme: 'light' })
      })

75. Vue3 中如何实现路由守卫?

Vue3 中路由守卫的用法与 Vue2 类似,但需结合 Vue Router 4+ 的 API,常用守卫包括:

  1. 全局守卫

    javascript 复制代码
    // router/index.js
    import { createRouter } from 'vue-router'
    const router = createRouter(/* 配置 */)
    
    // 全局前置守卫(跳转前触发)
    router.beforeEach((to, from, next) => {
      if (to.meta.requiresAuth && !isLogin) {
        next('/login') // 未登录则跳转到登录页
      } else {
        next() // 允许跳转
      }
    })
    
    // 全局后置守卫(跳转后触发)
    router.afterEach((to, from) => {
      document.title = to.meta.title || '默认标题'
    })
  2. 路由独享守卫 (在路由配置中定义):

    javascript 复制代码
    const routes = [
      {
        path: '/admin',
        component: Admin,
        beforeEnter: (to, from, next) => { // 仅当前路由生效
          if (!isAdmin) next('/forbidden')
          else next()
        }
      }
    ]
  3. 组件内守卫

    vue 复制代码
    <script setup>
    import { onBeforeRouteEnter, onBeforeRouteLeave } from 'vue-router'
    
    // 进入组件前触发(此时组件实例未创建,无法访问 this)
    onBeforeRouteEnter((to, from, next) => {
      next(vm => { // 通过 vm 访问组件实例
        vm.fetchData()
      })
    })
    
    // 离开组件前触发
    onBeforeRouteLeave((to, from, next) => {
      if (confirm('确定离开吗?')) next()
      else next(false) // 取消跳转
    })
    </script>