跳到正文
前端知识库
Vue3

Vue3 面试真题补充:迁移与运行时

补充 Vue2 到 Vue3 的生命周期、组件属性、v-model、Teleport、渲染器和 SSR 迁移边界。

4 分钟Vue3 · Composition API · Teleport · 迁移 · 面试

Vue3 面试真题补充:迁移与运行时

本文整理编号 3《Vue 面试真题》中 Vue3 部分,重点放在迁移时真正需要做决策的差异。

1. 生命周期迁移

Vue2 Vue3 Composition API 说明
beforeCreate setup 执行阶段 组合式逻辑在实例完成前建立
created setup 执行阶段 不再依赖 this 访问状态
beforeMount onBeforeMount 挂载前
mounted onMounted DOM 挂载后
beforeUpdate onBeforeUpdate 更新提交前
updated onUpdated DOM 更新后
beforeDestroy onBeforeUnmount 卸载前
destroyed onUnmounted 卸载完成后

setup 中不能使用组件实例上的 this。需要访问 DOM 时,用 template ref,并在 onMounted 之后读取;需要订阅外部资源时,在对应 hook 中注册并在卸载 hook 中清理。(知识点:生命周期与模板引用

2. setup 与响应式边界

<script setup>
import { computed, onMounted, ref } from 'vue'

const count = ref(0)
const doubled = computed(() => count.value * 2)

onMounted(() => {
  console.log('mounted')
})

function increment() {
  count.value += 1
}
</script>

模板会自动解包顶层 ref,JavaScript 中仍需使用 .valuereactive 适合紧密关联的对象,但直接解构会失去响应性;需要解构时使用 toRefstoRef。不要把一个 reactive 绑定整体替换成新对象,若需要替换,使用 ref 包装对象。(知识点:代理身份与响应式边界

3. 组件属性与事件

Vue3 建议显式声明 propsemits,让组件契约和类型推断可见:

<script setup>
const props = defineProps({
  modelValue: { type: String, default: '' },
})

const emit = defineEmits(['update:modelValue', 'submit'])

function update(value) {
  emit('update:modelValue', value)
}
</script>

未声明的属性和监听器会进入 $attrs。如果组件有多个根节点,要明确把 $attrs 绑定到哪个根元素,否则属性不会自动继承:

<template>
  <main v-bind="$attrs">
    <slot />
  </main>
</template>

Vue2 的 $listeners 在 Vue3 中已并入 $attrs。组件应通过 emits 声明事件,避免把事件误当作普通属性继续透传。(知识点:组件通信与透传边界

4. v-model 的新契约

Vue3 默认的组件双向绑定使用 modelValueupdate:modelValue

<!-- 父组件 -->
<SearchBox v-model="query" />
<!-- 子组件 -->
<input
  :value="modelValue"
  @input="$emit('update:modelValue', $event.target.value)"
>

多个绑定可以使用参数,例如 v-model:title 对应 titleupdate:title。这比 Vue2 的 .sync 更明确,也便于声明多个独立的双向绑定。(知识点:v-model

5. Teleport 的使用边界

Teleport 将组件模板的一部分渲染到当前组件 DOM 层级之外:

<Teleport to="body">
  <div v-if="open" class="modal">
    <button @click="open = false">关闭</button>
  </div>
</Teleport>

它适合 Modal、Toast、全局下拉层等容易被祖先 overflow 或层叠上下文限制的内容。Teleport 只改变 DOM 挂载位置,不改变组件逻辑上的父子关系,事件和依赖注入仍沿组件树工作。目标节点不存在或多个实例争用同一容器时,应在应用入口明确管理容器。(知识点:Teleport 的作用

6. v-ifv-show 与更新时机

v-if 控制子树是否创建,v-show 只控制显示样式。频繁切换的大型弹层可以使用 v-show 或 Teleport 配合缓存;一次性权限分支则更适合 v-if

多次状态修改会被合并到更新队列。需要读取更新后的 DOM 时使用 nextTick

message.value = 'updated'
await nextTick()
const height = panel.value?.offsetHeight

不要用 setTimeout 猜测渲染时机;nextTick 表达的是“等待当前响应式更新完成”。(知识点:调度队列与 DOM 更新时间

7. API 与运行时变化

  • Vue.setVue.delete 不再需要,Proxy 会观察新增和删除。
  • 过滤器被移除,用方法、计算属性或格式化函数替代。
  • 全局组件、指令和插件通过应用实例注册:const app = createApp(App)
  • Vue.prototype 改为 app.config.globalProperties,但应优先使用显式 composable 或 provide/inject。
  • Vue3 的 runtime-core 与 runtime-dom 分离,运行时可以通过 createRenderer 适配 Canvas、原生控件等非 DOM 宿主。

自定义渲染器至少需要实现元素创建、文本节点创建、插入、删除、属性更新和事件绑定等宿主操作;业务组件和响应式系统仍由 Vue runtime-core 管理。(知识点:Vue3 源码架构优化

8. SSR 与迁移检查

SSR 应为每个请求创建独立的 app、router 和 store 实例,不能把请求状态放在模块级单例中。服务端预取的数据需要序列化到 HTML,再由客户端恢复,否则会出现请求串扰或 hydration 不一致。

从 Vue2 迁移到 Vue3 时,建议按下面顺序检查:

  1. 全局初始化和插件注册是否改为 createApp
  2. this、过滤器、$listenersVue.set 等旧 API 是否还有依赖。
  3. 组件事件是否声明 emitsv-model 是否改成 modelValue 契约。
  4. 使用 keep-alive、路由守卫和 SSR 时,生命周期名称及实例创建方式是否匹配。
  5. 第三方组件库是否已经提供 Vue3 版本,避免只改语法而保留不兼容运行时。(知识点:SSR 与 Hydration