Vue3 面试真题补充:迁移与运行时
补充 Vue2 到 Vue3 的生命周期、组件属性、v-model、Teleport、渲染器和 SSR 迁移边界。
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 中仍需使用 .value。reactive 适合紧密关联的对象,但直接解构会失去响应性;需要解构时使用 toRefs 或 toRef。不要把一个 reactive 绑定整体替换成新对象,若需要替换,使用 ref 包装对象。(知识点:代理身份与响应式边界)
3. 组件属性与事件
Vue3 建议显式声明 props 和 emits,让组件契约和类型推断可见:
<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 默认的组件双向绑定使用 modelValue 和 update:modelValue:
<!-- 父组件 -->
<SearchBox v-model="query" />
<!-- 子组件 -->
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
>
多个绑定可以使用参数,例如 v-model:title 对应 title 和 update: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-if、v-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.set、Vue.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 时,建议按下面顺序检查:
- 全局初始化和插件注册是否改为
createApp。 this、过滤器、$listeners、Vue.set等旧 API 是否还有依赖。- 组件事件是否声明
emits,v-model是否改成modelValue契约。 - 使用
keep-alive、路由守卫和 SSR 时,生命周期名称及实例创建方式是否匹配。 - 第三方组件库是否已经提供 Vue3 版本,避免只改语法而保留不兼容运行时。(知识点:SSR 与 Hydration)