2025前端面试题-Vue3基础篇

2,016 阅读9分钟

以下为Vue3面试考察点总结,具体知识点不会太详细,主要梳理面试核心考察点,为面试做准备。

一、生命周期

核心变化

1.组合式API的引入

  • setup()替代beforeCreate和created。所有组合式API逻辑在此函数中初始化,替代Vue2的data、method等选项式配置
  • 钩子函数前缀on。生命周期函数需要从Vue显示导入,如onMounted,且只能在setup()或<script setup>中使用。

2.钩子函数重命名

  • beforeDestroy -> onBeforeUnmount
  • destroyed -> onUnmounted

3.新增钩子

  • onServerPrefetch。服务端渲染(SSR)期间异步获取数据。
  • 调试钩子。onRenderTracked(跟踪响应式依赖)、OnRenderTriggered(响应式变更触发)。

生命周期阶段详解

1.初始化阶段

  • setup()。替代beforeCreated和created,初始化响应式数据、方法等。注意:无法访问this。
  • 选项式API兼容。替代beforeCreated和created仍可用,但避免与setup()混用。

2.挂载阶段

  • onBeforeMount。组件挂载到DOM前调用,此时虚拟DOM已生成但未渲染。
  • onMounted。组件挂载完成,可操作DOM或发起网络请求。

3.更新阶段

  • onBeforeUpdate。数据变化导致DOM更新前触发,适合获取更新前的DOM状态。
  • onUpdated。DOM更新执行后,避免在此修改状态,可能导致无限循环

4.卸载阶段

  • onBeforeUnmount。组件卸载前调用,清理定时器,取消网络请求,移除事件监听。
  • onUnMounted。组件卸载后触发,此时子组件已全部卸载。

5.其他钩子

  • onErrorCaptured。捕获子孙组件错误,可返回false阻止冒泡。
  • onActivated/onDeactivated。<KeepAlive>缓存组件激活/停用时调用。

高频面试题

1.父子组件生命周期执行顺序

  • 挂载阶段父onBeforeMount->子onBeforeMount->子onMounted->父onMounted
  • 更新阶段父onBeforeUpdate->子onBeforeUpdate->子onUpdated->父onUpdated
  • 卸载阶段父onBeforeUnmount->子onBeforeUnmount->子onUnmounted->父onUnmounted

2.在setup()中如何访问this?

  • setup()中没有this,响应式数据通过ref/reactive定义,方法直接申明。

3.异步请求放在哪个钩子?

  • 客户端渲染(CSR):onMounted(确保DOM可用)。
  • 服务端渲染(SSR):onServerPrefetch。

二、组件通信

1.Props/Emits(父子通信)

props(父->子)

<!-- 父组件 -->
<Child :title="data" />
<!-- 子组件 -->
<script setup>
defineProps(['title'])
</script>
  • 类型校验defineProps({ title:{ type:String, required:true } })
  • 单项数据流:子组件不能直接修改props(需要通过emit通知父组件)。

emits(子->父)

<!-- 子组件 -->
<button @click="$emit('update', value)" ></button>
<!-- 父组件 -->
<Child @update="handleUpdate" />
  • Vue3特性defineEmits(['update'])显式申明事件。

2.v-model双向绑定(语法糖进阶)

  • 单值绑定
<!-- 父组件 -->
<Child v-model="message" />
<!-- 子组件 -->
<input :value="modelValue" @input="$emit('update:modelValue',$event.target.value)" >
<script setup>
defineProps(["modalValue"])
defineEmits(["update:modelValue"])
</script>
  • 多值绑定
<Child v-model:title="title" v-model:content="content" />

3.ref/expose(父访问子组件)

  • 模版引用
<!-- 父组件 -->
<Child ref="childRef" />
<script setup>
const childRef = ref(null);
// 访问子组件暴露的属性/方法
childRef.value.childmethod();
</script>

<!-- 子组件 -->
const childMethod = () => {};
defineExpose({ childMethod })
</script>

4.provide/inject(跨层级通信)

  • 依赖注入
// 祖先组件
import { provide } from 'vue';
provide('theme', 'dark');

// 后代组件
import { inject } from 'vue';
// 第二个参数为默认值
const theme = inject('theme', 'light')
  • 响应式数据
// 提供响应式数据
const count = ref(0);
provide('count', count);

5.事件总线(Event Bus)

  • Vue3官方废弃$on,推荐第三方库(如mitt)
// eventBus.js
import mitt from 'mitt';
export const emitter = mitt();

// 组件A(发布事件)
emitter.emit('refresh', data);

// 组件B(订阅事件)
emitter.on('refresh', (data) => { /* ... */ })

// 组件卸载时取消订阅
onUnmounted(() => emitter.off('refresh'))

6.状态管理(pinia)

  • 替代Vuex的官方状态库
// store/counter.js
export const useCounterStore = defineStore('counter',{
   state:()=>({ count: 0 }),
   actions:{
       increment() { this.count++; }
   }
})

// 组件中使用
const store = useCounterStore();
store.increment();

7.属性透传($attr)

  • 透传非props属性
<!-- 父组件 -->
<Child class="child-style" data-id="123" />

<!-- 子组件 -->
<div v-bind="$attrs"></div>
<script setup>
// 禁用自动继承
defineOptions({ inheritAttrs: false });
</script>

8.模版引用(Template Refs)

  • 直接操作DOM
<template>
    <input ref="inputRef" />
</template>

<script setup>
const inputRef = ref(null);
onMounted(() => inputRef.value.focus())
</script>

高频面试题

1.父子组件通信方式有哪些?

  • 父->子:props、$attr、ref。
  • 子->父:emits、v-model。
  • 双向:v-model、状态管理。

2.provide/inject能否替代Vuex?

  • 适用场景:跨层级但关系明确的组件。
  • 局限性:不适合全局状态管理,无法跟踪状态变化历史。

3.Vue3为什么不移除事件总线?

  • 设计理念:避免全局事件导致代码维护困难。
  • 代替方案:使用pinia管理状态或mitt库实现事件总线。

4.如何实现兄弟组件通信?

  1. 通过共同父组件中转(props/emits)
  2. 事件总线(mitt)
  3. 状态管理(Pinia)

5.动态组件如何保持状态?

  • <KeepAlive>组件
<KeepAlive>
    <component :is="currentComponent" />
</KeepAlive>

实战场景选择

场景推荐方案
父子简单数据传递Props/Emits
表单双向绑定v-model语法糖
跨层级组件共享配置provide/inject
复杂全局状态管理Pinia
非父子组件松散通信事件总线(mitt)
组件模板直接操作ref/expose

三、修饰符

核心修饰符分类及作用

1.事件修饰符

  • stop:阻止事件冒泡。
<button @click.stop="handleClick">点击</button>
  • prevent:阻止默认行为。
<form @submit.prevent="onSubmit"></form>
  • capture:使用捕获模式。
<div @click.capture="handleCapture">捕获触发</div>
  • self:仅当事件从元素自身触发时执行。
<div @click.self="handleSelf">仅自身点击有效</div
  • once:事件只触发一次。
<button @click.once="handleOnce">只触发一次</button>
  • passive:提升滚动性能,不阻止默认行为。
<div @scroll.passive="onScroll">滚动优化</div>

2.v-model修饰符

  • lazy:输入框失焦后更新数据(替代input为change事件)。
<input v-model.lazy="message" />
  • number:将输入值转为数值类型。
<input v-model.number="age" type="number" />
  • trim:自动去除首位空格。
<input v-model.trim="username" />

3.键盘修饰符

  • 键名修饰符:直接使用按键名(如:.enter,.tab,.esc)。
<input @keyup.enter="submit" />
  • 系统修饰键:.ctrl,.alt,.shift,.meta(MAC的Command健)
<button @click.ctrl="handleCtrlClick">需按住 Ctrl 点击</button>
  • .exact:精确匹配系统修饰键组合。
<button @click.ctrl.exact="onlyCtrl">仅 Ctrl 按下时触发</button>

4.鼠标修饰符

  • .left,.right,.middle:限制鼠标按键。
<div @mousedown.right="handleRightClick">右键点击</div>

Vue3修饰符新特性

1..sync修饰符的替代

  • Vue2:通过.sync实现父子组件双向绑定。
  • Vue3:改用v-model:propName+emit('update:propName')。
<!-- 父组件 -->
<Child v-model:title="pageTitle" />

<!-- 子组件 -->
<script setup>
defineProps(['title'])
defineEmits(['update:title'])
</script>

2..native修饰符移除

  • Vue2:组件上绑定原生事件需用.native。
  • Vue3:默认不绑定到根元素,需通过emits声明或手动绑定。
<!-- 父组件 -->
<Child @click="handleClick" /> <!-- 需子组件 emit('click') -->

3.自定义组件支持v-model修饰符

  • 通过modelModifiers访问修饰符。
<!-- 父组件 -->
<CustomInput v-model.capitalize="text" />

<!-- 子组件 -->
<script setup>
const props = defineProps(['modelValue', 'modelModifiers'])
const emit = defineEmits(['update:modelValue'])

const handleInput = (e) => {
  let value = e.target.value
  if (props.modelModifiers.capitalize) {
    value = value.charAt(0).toUpperCase() + value.slice(1)
  }
  emit('update:modelValue', value)
}
</script>

高频面试题

1.v-model的.lazy和.sync有何区别?

  • .lazy:延迟数据同步(input->change事件)。
  • .sync:Vue2中用于父子组件双向绑定,Vue3改用v-model:prop。

2.如何阻止事件冒泡和默认行为?

  • 链式调用:@click.stop.prevent(顺序不影响效果)。

3.Vue3如何监听组件原生事件?

  • 子组件手动绑定并emit事件
  • 使用v-on="$attrs"将事件绑定到内部元素。

4.如何实现自定义v-model修饰符?

  • 通过modelModifiers判断修饰符存在,并调整数据逻辑。

5..exact修饰符的作用是什么?

  • 精确控制系统修饰键组合,避免其他修饰键按下触发。

四、指令

核心内置指令

1.数据绑定

  • v-bind:动态绑定属性(缩写 :)。
<img :src="imageUrl" :alt="altText">
<!-- 动态属性名 -->
<div :[dynamicAttr]="value"></div>
  • v-model:表单双向绑定(组合式API增强)。
<input v-model="text">  <!-- 默认对应 `modelValue` -->
<!-- 自定义组件多v-model -->
<CustomInput v-model:title="title" v-model:content="content" />

2.条件渲染

  • v-if/v-else-if/v-else:动态添加/移除DOM元素。
<div v-if="score > 90">A</div>
<div v-else-if="score > 60">B</div>
<div v-else>C</div>
  • v-show:通过css切换显示(display:none)。
  • v-if与v-show的区别:v-show初始渲染成本高,频繁切换时性能更好。

3.列表渲染

  • v-for:遍历数组或对象
<li v-for="(item, index) in items" :key="item.id">{{ index }}: {{ item.name }}</li>
  • 必须指定key:优化虚拟DOM复用,避免渲染错误。
  • 避免与v-if共用:优先级问题(Vue2中v-for优先,Vue3中v-if优先),应改用计算属性过滤数据。

4.事件绑定

  • v-on:监听事件(缩写 @)
<button @click="handleClick">点击</button>
<!-- 事件修饰符 -->
<form @submit.prevent="onSubmit"></form>

5.其他指令

  • v-html:渲染原始HTML(警惕XSS攻击)
  • v-text:替代 {{ }} 插值
  • v-pre:跳过编译,保留原始内容
  • v-cloak:隐藏未编译的模版(配合css使用)
[v-cloak] { display: none; }
  • v-once:仅渲染一次,后续数据变化不更新。
<div v-once>{{ staticContent }}</div>

Vue3指令新特性

1.v-model增强

  • 支持多个v-model
<UserForm v-model:name="name" v-model:age="age" />
  • 自定义修饰符:通过modelModifiers访问
defineProps(['modelValue', 'modelModifiers']);
if (props.modelModifiers.capitalize) {
  // 处理修饰符逻辑
}

2.v-bind合并行为

  • 同名属性合并策略:Vue3中后绑定的属性会覆盖前面的,而Vue2会合并。

3.v-for中的ref处理

  • ref数组不再自动创建,需手动处理:
<div v-for="item in list" :ref="setItemRef"></div>
<script setup>
const itemRefs = [];
const setItemRef = el => { if (el) itemRefs.push(el); };
</script>

自定义指令

1.注册方式

  • 全局注册
app.directive('focus', {
  mounted(el) { el.focus(); }
});
  • 局部注册(组合式API)
<script setup>
const vFocus = { mounted: (el) => el.focus() };
</script>

2.指令生命周期钩子

  • created:元素属性初始化前
  • beforeMount:元素插入DOM前(Vue2的bind
  • mounted:元素插入DMO后(Vue2的inserted
  • beforeUpdate:组件更新前
  • update:组件更新后
  • beforeUnmount:组件卸载前(Vue2的unbind
  • unMounted:组件卸载后

3.指令参数

  • el:绑定的DOM元素
  • binding:绑定以下属性:
    • value:指令的绑定值(如v-dir="value"
    • oldValue:旧值(仅在beforeUpdate和update可用
    • arg:指令参数(如v-dir:arg
    • modifiers:修饰符对象(如v-dir.modif->{ modif: true }
    • instance:组件实例(替代Vue2的vnode.context

实战实例

  • 自动聚焦指令
const vFocus = {
  mounted(el) { el.focus(); }
};
  • 权限控制指令
const vPermission = {
  mounted(el, binding) {
    const roles = store.getters.roles;
    if (!roles.includes(binding.value)) {
      el.parentNode?.removeChild(el);
    }
  }
};

高频面试题

1.v-if和v-show的区别

  • v-if:条件为假时销毁DOM,适合不频繁切换场景
  • v-show:始终保留DOM,通过css切换显示,适合频繁切换

2.为什么v-for需要key?

  • 虚拟DOM优化:key帮助Vue识别节点身份,避免错误使用复用元素,提升更新效率。

3.如何实现自定义指令?

  • 定义指令对象并注册,通过生命周期钩子操作DOM(如自动聚焦、权限控制)。

4.v-model在自定义组件中的实现原理?

  • 父组件:v-model:propName="value"
  • 子组件:接收propName,通过emit('update:propName',value)更新

5.Vue3中v-bind的合并策略变化

  • Vue3中后绑定的属性会覆盖前面的同名属性,而Vue2会合并(如class和style)

五、ref和reactive的区别

核心区别与使用场景

​特性​refreactive
​适用数据类型​基本类型(string/number/boolean)或对象对象或数组
​访问方式​通过 .value 访问直接访问属性
​响应式原理​内部对对象类型调用 reactive基于 Proxy 的深层代理
​模板自动解包​在模板中无需 .value直接使用属性
​解构响应性​需用 toRefs 保持响应性直接解构会丢失响应性,需用 toRefs

核心面试题

1.为什么ref需要.value?

  • 设计目的:统一处理基本类型和对象类型。

    • 基本类型无法通过Proxy代理,ref通过封装对象({ value:... })实现响应式
  • 底层实现

function ref(value) {
  return { 
    __v_isRef: true,
    get value() { track(this, 'value'); return value; },
    set value(newVal) { value = newVal; trigger(this, 'value'); }
  };
}

2.如何选择ref和reactive?

  • 优先ref
    • 管理基本类型数据
    • 需要明确的数据引用(如传递到函数中仍保持响应性)
  • 优先reactive
    • 管理复杂对象/数组,避免频繁使用.value
    • 需要深层嵌套的响应式数据

3.如何解构reactive对象且保持响应性?

  • 使用toRefs
const state = reactive({ count: 0, name: 'Vue' });
const { count, name } = toRefs(state); // 保持响应性
count.value++; // 生效

4.reactive的局限性是什么?

  • 无法直接替换整个对象
let obj = reactive({ a: 1 });
obj = { a: 2 }; // 响应式丢失!
  • 解决方案:使用Object.assign或ref包裹对象
Object.assign(obj, { a: 2 }); // 保持响应性
const objRef = ref({ a: 1 }); // 替换整个对象时响应式有效

5.ref如何处理对象类型

  • 自动调用reactive
const objRef = ref({ count: 0 });
objRef.value.count++; // 响应式生效

6.如何使用ref实现防抖功能?

  • 自定义customRef
function debouncedRef(value, delay = 200) {
    let timeout;
    return customRef((track, trigger) => ({
        get(){
            track();
            return value;
        }
        set(newVal){
            clearTimeout(timeout);
            timeout = setTimeout(() => {
                value = newVal;
                tigger();
            }, delay);
        }
    }))
}
// 使用
const text = debouncedRef('', 500);

原理深入

ref的响应式实现

  • 基本类型:通过 RefImpl 类实现,而 RefImpl 类内部使用了 JavaScript 原生的 getter/setter 语法(不是直接调用Object.defineProperty)。
  • 对象类型:内部转化为reactive代理

reactive的响应式实现

  • 基于Proxy代理整个对象,递归处理嵌套熟悉
  • 依赖收集:在get时调用track收集依赖
  • 触发更新:在set时调用trigger通知更新

高频面试题

1.ref和reactive底层实现差异

  • ref封装value属性,reactive使用Proxy代理整个对象

2.为什么解构reactive对象会失去响应性?

  • 解构得到的是普通值,非响应式引用;使用toRefs转化为ref

3.如何在模版中正确使用ref?

  • 直接使用变量名(自动解包.value),但嵌套在对象中需手动解包
<template>
  {{ count }} <!-- 自动解包 -->
  {{ objRef.count }} <!-- 需确保 objRef 是 reactive 或解包后的 ref -->
</template>

4.如何监听ref或reactive的变化?

  • 使用watch或watchEffect:
watch(countRef, (newVal) => { /* ... */ });
watch(() => state.count, (newVal) => { /* ... */ });

5.ref和reactive的性能差异

  • 基本类型:ref 更高效(无需 Proxy 代理)。
  • 对象类型:性能差异可忽略。

六、slot插槽

核心概念

1. 默认插槽

  • 父组件:传递内容到子组件默认位置
<Child>默认内容</Child>
  • 子组件:通过<slot>接收内容
<template>
 <div>
    <slot>Fallback Content</slot> <!-- 后备内容 -->
 </div>
</template>

2.具名插槽

  • 父组件:用v-slot:name#name指定插槽名
 <Child>
    <template #header>头部内容</template>
    <template #default>默认内容</template>
    <template #footer>底部内容</template>
</Child>
  • 子组件:用<slot name="header">定义具名插槽
<template>
     <slot name="header"></slot>
     <slot></slot> <!-- 默认插槽 -->
     <slot name="footer"></slot>
</template>

3.作用域插槽

  • 子组件:通过<slot>传递数据
<template>
  <slot :user="user" :data="data"></slot>
</template>
  • 父组件:用v-slot="props"接收数据
<Child>
  <template #default="slotProps">
    {{ slotProps.user.name }}
  </template>
</Child>
  • 解构语法
<template #default="{ user, data }">
  {{ user.age }}
</template>

Vue3插槽新特性

1.统一语法

  • 废弃slot属性:改用v-slot指令
  • 废弃slot-scope:统一使用v-slot#语法

2.动态插槽名

<template #[dynamicSlotName]>
  动态插槽内容
</template>

3.$slots API变化

  • Vue2this.$slotsthis.$scopedSlots分离
  • Vue3:统一为this.$slots,作用域插槽通过函数调用
// 子组件中访问
this.$slots.header?.() // 返回VNode数组

高频面试题

1.作用域插槽的作用是什么?

  • 数据反向传递:允许子组件向父组件传递数据,实现内容渲染逻辑的定制

2.如何传递多个插槽?

  1. 多个<template>分别指定插槽名
  2. 通过v-bind合并插槽内容(高阶组件)
<Child v-slot="{ data }">
  <template #header>Header {{ data }}</template>
  <template #footer>Footer {{ data }}</template>
</Child>

3. 动态插槽的应用场景

  • 根据状态动态切换插槽内容,如表格组件根据数据类型渲染不同列

4. slot的name属性是否支持动态绑定?

  • 支持:<slot :name="dynamicName">,但父组件需要用动态插槽名接收

高级用法和原理

1.渲染作用域

  • 插槽内容在父组件作用域编译,只能访问父组件数据(除非通过作用域插槽传递子数据)

2.作用域插槽实现原理

  • 子组件:将数据作为函数参数传递给插槽
// 子组件编译后
render() {
  return h('div', this.$slots.default({ user: this.user }))
}

3.插槽性能优化

  • 避免在插槽内容中使用复杂计算,必要时用v-once缓存静态内容

实战实例

<!-- 子组件 ScopedList.vue -->
<template>
  <ul>
    <li v-for="item in items" :key="item.id">
      <slot :item="item" :index="index">
        默认内容:{{ item.name }}
      </slot>
    </li>
  </ul>
</template>

<!-- 父组件 -->
<ScopedList :items="list">
  <template #default="{ item, index }">
    {{ index + 1 }}. {{ item.name }} (ID: {{ item.id }})
  </template>
  
  <template #footer> <!-- 具名插槽 -->
    <div>Total: {{ list.length }}</div>
  </template>
</ScopedList>

七、watch、watchEffect、computed的区别

核心区别

特性computedwatchwatchEffect
用途派生响应式数据监听数据变化,执行副作用操作自动收集依赖,执行副作用操作
返回值只读的 Ref 对象返回停止监听的函数返回停止监听的函数
依赖收集自动收集依赖,惰性计算(缓存结果)需显式指定监听源自动收集依赖,立即执行
新旧值获取可获取旧值和新值无旧值,只跟踪最新值
执行时机依赖变化时重新计算默认在组件更新前执行(flush: 'pre'默认在组件更新前执行(类似 watch
异步处理不支持支持异步操作支持异步操作

核心使用场景

1.computed

  • 场景:基于响应式数据生成新的值(如过滤列表、计算总和)
const fullName = computed(() => `${firstName.value} ${lastName.value}`);

2.watch

  • 场景:监听特定数据变化,执行异步或复杂逻辑(如API请求、验证)
watch(userId, async (newId, oldId) => {
  const data = await fetchUser(newId);
  userData.value = data;
}, { immediate: true }); // 立即执行一次

3.watchEffect

  • 场景:自动跟踪依赖变化,执行副作用操作(如日志、DOM操作)
watchEffect(() => {
  console.log(`窗口大小:${window.innerWidth}x${window.innerHeight}`);
  document.title = `Count: ${count.value}`;
});

高频面试题

1.三者核心区别是什么?

  • computed:派生数据,有缓存,惰性计算
  • watch:显式监听数据源,支持新旧值对比和异步
  • watchEffect:自动收集依赖,立即执行,无旧值

2.watch和watchEffect的依赖收集方式有何不同?

  • watch:需显式指定监听目标(如() => state.a
  • watchEffect:自动收集回调函数内使用的所有响应式依赖

3.如何停止watch或watchEffect的监听?

  • 调用它们返回的停止函数
const stop = watch(data, callback);
stop(); // 停止监听

4.computed和普通函数的区别

  • computed会缓存结果,依赖不变时不重新计算;普通函数每次调用时都会执行

5.watch的immediate和deep选项的作用

  • immediate:true:立即执行回调(初始值触发)
  • deep:true:深度监听对象/数组内部变化

6.什么情况下使用watchEffect替代watch?

  • 当依赖项不明确或需要自动跟踪多个依赖时(如同时监听多个状态)

底层原理与性能优化

1.computed缓存机制

  • 内部通过dirty标志位标记是否需要重新计算,依赖未变化时直接返回缓存值

2.watch的异步调度

  • 默认在组件更新前执行(flush:'pre'),可配置'post'(组件更新后)或'sync'(同步执行)

3.watchEffect的依赖收集

  • 在首次执行回调时收集所有响应式依赖(类似Vue2的watcher依赖收集)

4.性能注意事项

  • 避免过度使用watchEffect:自动依赖收集可能导致不必要的重复执行
  • 合理使用computed缓存:减少重复计算开销
  • 及时清理副作用:在onUnmounted中停止监听,避免内存泄漏。

代码实例对比

1.computed VS watch

// 计算总价(推荐用 computed)
const total = computed(() => items.value.reduce((sum, item) => sum + item.price, 0));

// 监听总价变化(watch 不适用于此类场景)
watch(total, (newVal) => {
  console.log("总价变化:", newVal); // 此时 computed 更高效
});

2.watch VS watchEffect

// watch:显式监听多个依赖
watch([a, b], ([newA, newB], [oldA, oldB]) => {
  console.log(`a从${oldA}变为${newA}, b从${oldB}变为${newB}`);
});

// watchEffect:自动收集依赖
watchEffect(() => {
  console.log(`a=${a.value}, b=${b.value}`); // 自动跟踪 a 和 b
});

Vue3新增特性

1.watch支持监听多个数据源

watch([ref1, () => reactiveObj.prop], ([val1, val2]) => { /* ... */ });

2.watchPostEffect和watchSyncEffect

  • watchPostEffect:等同于watchEffect(..., { flush: 'post' }),在DOM更新后执行。
  • watchSyncEffect:等同于watchEffect(..., { flush: 'sync' }),同步执行

总结

  • computed:用于派生数据,优先使用
  • watch:用于监听特定变化数据,需精确控制依赖
  • watchEffect:用于自动依赖跟踪,简化副作用管理

以上是Vue3面试题的基础篇内容,如有错误欢迎评论区指正,后续还会更新Vue3进阶篇。