Vue3面试核心问题
目录
Vue3新特性
1. Vue3有哪些新特性?
参考答案:
| 新特性 |
说明 |
| Composition API |
组合式API,更灵活的代码组织 |
| Proxy响应式 |
替代Object.defineProperty |
| Teleport |
传送到任意DOM |
| Suspense |
异步组件加载状态 |
| Fragment |
多根节点组件 |
| Tree-shaking |
更多API按需引入 |
| 更小更快 |
优化后的打包体积 |
| TypeScript |
完整类型系统支持 |
| 自定义渲染器 |
自定义渲染API |
2. setup函数是什么?
参考答案:
setup是Vue3新增的入口函数,在beforeCreate之前执行:
1 2 3 4 5 6 7 8 9 10 11
| export default { setup() { const count = ref(0)
return { count } } }
|
执行时机:
- 在beforeCreate之前执行
- 每当props更新后会重新执行
- 不能使用this
3. script setup是什么?
参考答案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <script setup> // 编译器自动转换为setup函数内容 import { ref } from 'vue'
const count = ref(0)
// 导入的组件自动注册 import MyComponent from './MyComponent.vue'
// 顶层变量和函数直接在模板使用 function increment() { count.value++ } </script>
|
优势:
4. Teleport是什么?
参考答案:
Teleport可以将组件渲染到任意DOM位置:
1 2 3 4 5 6 7 8 9 10 11 12
| <template> <teleport to="body"> <div class="modal"> <p>模态框内容</p> </div> </teleport> </template>
<!-- 条件控制 --> <teleport to="body" :disabled="!isShow"> <div v-if="isShow" class="modal">内容</div> </teleport>
|
5. Suspense是什么?
参考答案:
Suspense用于异步组件加载状态:
1 2 3 4 5 6 7 8 9 10
| <template> <suspense> <template #default> <async-component /> </template> <template #fallback> <div>加载中...</div> </template> </suspense> </template>
|
Composition API
6. ref和reactive的区别?
参考答案:
| 对比 |
ref |
reactive |
| 适用类型 |
基本类型 |
对象/数组 |
| 访问方式 |
.value |
直接访问 |
| 解构 |
丢失响应式(需toRefs) |
丢失响应式 |
| 重新赋值 |
会响应式更新 |
会响应式更新 |
| 内部实现 |
对象包装 |
Proxy代理 |
1 2 3 4 5 6 7
| const count = ref(0) count.value++
const state = reactive({ count: 0 }) state.count++
|
7. toRefs的作用?
参考答案:
toRefs用于将reactive对象转换为普通对象,保持响应式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const state = reactive({ count: 0, name: '张三' })
const { count, name } = toRefs(state)
const stateAsRefs = toRefs(state)
count.value++ name.value = '李四'
|
8. computed和watch的区别?
参考答案:
| 对比 |
computed |
watch |
| 用途 |
计算属性 |
监听数据变化 |
| 返回值 |
必须有 |
可有可无 |
| 缓存 |
有 |
无 |
| 执行时机 |
被动响应 |
主动监听 |
| 适用场景 |
同步计算 |
异步/副作用 |
1 2 3 4 5 6 7 8 9 10 11 12
| const doubled = computed(() => count.value * 2)
watch(count, (newVal, oldVal) => { console.log(newVal, oldVal) })
watchEffect(() => { console.log(count.value) })
|
9. watch和watchEffect的区别?
参考答案:
| 对比 |
watch |
watchEffect |
| 依赖收集 |
手动指定 |
自动收集 |
| 立即执行 |
需要immediate |
默认立即执行 |
| 访问旧值 |
可以 |
不可以 |
| 首次执行 |
可能不执行 |
立即执行 |
| 适用场景 |
精确控制 |
副作用操作 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| watch(count, (newVal, oldVal) => { })
watch(count, (newVal, oldVal) => { }, { immediate: true })
watchEffect(() => { console.log(count.value) })
|
10. provide/inject的使用场景?
参考答案:
用于跨级组件通信:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import { provide, ref } from 'vue'
export default { setup() { const theme = ref('dark') provide('theme', theme)
function updateTheme() { theme.value = 'light' } provide('updateTheme', updateTheme) } }
import { inject } from 'vue'
export default { setup() { const theme = inject('theme') const updateTheme = inject('updateTheme')
return { theme, updateTheme } } }
|
响应式原理
11. Vue3响应式原理是什么?
参考答案:
Vue3使用Proxy实现响应式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const proxy = new Proxy(data, { get(target, key, receiver) { const res = Reflect.get(target, key, receiver) track(target, key) return typeof res === 'object' ? reactive(res) : res }, set(target, key, value, receiver) { const res = Reflect.set(target, key, value, receiver) trigger(target, key) return res }, deleteProperty(target, key) { const res = Reflect.deleteProperty(target, key) trigger(target, key) return res } })
|
12. Proxy相比Object.defineProperty的优势?
参考答案:
| 优势 |
说明 |
| 监听整个对象 |
无需递归 |
| 监听新增属性 |
自动响应 |
| 监听删除属性 |
自动响应 |
| 监听数组索引 |
自动响应 |
| 支持Map/Set |
新的数据结构 |
| 更好的性能 |
一次代理 |
13. 什么是依赖收集?
参考答案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class ReactiveEffect { constructor(fn) { this.fn = fn this.deps = [] }
run() { activeEffect = this this.fn() activeEffect = null } }
function track(target, key) { if (activeEffect) { targetMap.get(target).get(key).add(activeEffect) activeEffect.deps.push(targetMap.get(target).get(key)) } }
function trigger(target, key) { targetMap.get(target).get(key).forEach(effect => effect.run()) }
|
14. readonly和shallowReactive?
参考答案:
| API |
说明 |
| reactive |
深层响应式 |
| shallowReactive |
浅层响应式 |
| ref |
深层响应式(基本类型) |
| shallowRef |
浅层响应式 |
| readonly |
只读响应式 |
| shallowReadonly |
浅层只读 |
1 2 3 4 5 6 7 8 9 10 11
| const state = shallowReactive({ count: 0, deep: { name: '张三' } }) state.deep.name = '李四'
const list = shallowRef([]) list.value.push(1) list.value = [1]
|
15. 如何追踪Vue3的响应式依赖?
参考答案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import { watchEffect, onRenderTracked, onRenderTriggered } from 'vue'
export default { onMounted() { onRenderTracked((event) => { console.log('追踪:', event) })
onRenderTriggered((event) => { console.log('触发:', event) }) } }
|
与Vue2对比
16. Vue3生命周期有哪些变化?
参考答案:
| Vue2 |
Vue3 |
说明 |
| beforeCreate |
setup |
替代 |
| created |
setup |
替代 |
| beforeMount |
onBeforeMount |
- |
| mounted |
onMounted |
- |
| beforeUpdate |
onBeforeUpdate |
- |
| updated |
onUpdated |
- |
| beforeDestroy |
onBeforeUnmount |
改名 |
| destroyed |
onUnmounted |
改名 |
| errorCaptured |
onErrorCaptured |
- |
| - |
onRenderTracked |
新增 |
| - |
onRenderTriggered |
新增 |
17. Vue3全局API有哪些变化?
参考答案:
| Vue2 |
Vue3 |
说明 |
| Vue.config |
app.config |
- |
| Vue.component |
app.component |
- |
| Vue.directive |
app.directive |
- |
| Vue.mixin |
app.use |
移除,使用插件 |
| Vue.use |
app.use |
- |
| Vue.prototype |
app.config.globalProperties |
- |
| Vue.filter |
- |
移除 |
18. Vue3性能提升有哪些?
参考答案:
| 提升点 |
说明 |
| 打包体积 |
约10KB,比Vue2小 |
| 渲染速度 |
虚拟DOM重写,更快 |
| 更新性能 |
Proxy响应式,更精准 |
| 内存占用 |
优化后更小 |
| 编译优化 |
Block tree,静态提升 |
19. Vue3 Diff算法有什么优化?
参考答案:
Vue3的Diff算法优化:
- 静态提升: 静态节点不参与diff
- Patch flags: 标记动态节点
- Block tree: 区块树优化
- hoistStatic: 静态提升减少重复创建
1 2 3 4 5 6 7 8 9 10 11 12 13
| ┌─────────────────────────────────────────────────────────────┐ │ Vue3 Block Tree │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 静态节点直接提升,不参与diff │ │ │ │ <div> div (block) │ │ <p>静态内容</p> --> ├─ p (hoisted) │ │ <span>{{msg}}</span> ├─ span (patch flag) │ │ <span>{{msg}}</span> └─ span (patch flag) │ │ </div> │ │ │ └─────────────────────────────────────────────────────────────┘
|
20. Vue3的Tree-shaking如何实现?
参考答案:
Vue3支持Tree-shaking,可以排除未使用的功能:
1 2 3 4 5 6 7 8
| import { ref, computed, watch } from 'vue'
import { Transition } from 'vue'
|
实战问题
21. 如何在Vue3中使用Vue2的Options API?
参考答案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| export default { data() { return { count: 0 } }, methods: { increment() { this.count++ } }, computed: { doubled() { return this.count * 2 } },
setup() { const count = ref(0) return { count } } }
|
22. 如何实现逻辑复用?
参考答案:
Vue3使用Composables(组合式函数)实现逻辑复用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import { ref, onMounted, onUnmounted } from 'vue'
export function useMouse() { const x = ref(0) const y = ref(0)
function update(e) { x.value = e.pageX y.value = e.pageY }
onMounted(() => window.addEventListener('mousemove', update)) onUnmounted(() => window.removeEventListener('mousemove', update))
return { x, y } }
import { useMouse } from './useMouse'
export default { setup() { const { x, y } = useMouse() return { x, y } } }
|
23. 如何处理异步组件错误?
参考答案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const AsyncComp = defineAsyncComponent({ loader: () => import('./AsyncComp.vue'), errorComponent: ErrorComponent, delay: 200, timeout: 3000 })
import { onErrorCaptured } from 'vue'
export default { setup() { onErrorCaptured((err) => { console.error('捕获到错误:', err) return false }) } }
|
24. Vue3如何实现CSS变量主题切换?
参考答案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import { provide, ref, computed } from 'vue'
export default { setup() { const theme = ref('light')
const themeVars = computed(() => { if (theme.value === 'dark') { return { '--bg-color': '#1a1a1a', '--text-color': '#fff' } } return { '--bg-color': '#fff', '--text-color': '#000' } })
provide('theme', theme)
return { theme, themeVars } } }
|
1 2 3 4 5 6
| <!-- Child.vue --> <template> <div class="box" :style="themeVars"> 内容 </div> </template>
|
25. Vue3中如何操作DOM?
参考答案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <template> <div ref="domRef">内容</div> </template>
<script setup> import { ref, onMounted } from 'vue'
const domRef = ref(null)
onMounted(() => { console.log(domRef.value) // DOM元素 domRef.value.style.color = 'red' }) </script>
|
26. 如何升级Vue2项目到Vue3?
参考答案:
| 步骤 |
说明 |
| 1. 评估依赖 |
检查Vue插件兼容性 |
| 2. Vue CLI迁移 |
使用迁移工具 |
| 3. 全局API |
更新为app.config |
| 4. 过滤器 |
移除,使用方法替代 |
| 5. 组件 |
支持多根节点 |
| 6. 生命周期 |
更新为Composition API |
| 7. 测试 |
确保功能正常 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| new Vue({ el: '#app', router, store, render: h => h(App) })
import { createApp } from 'vue'
const app = createApp(App) app.use(router) app.use(store) app.mount('#app')
|
27. Vue3的v-memo是什么?
参考答案:
v-memo用于缓存模板子树:
1 2 3 4 5 6 7 8 9 10 11
| <!-- 只有valueA或valueB变化时才更新 --> <div v-memo="[valueA, valueB]"> <p>{{ valueA }}</p> <p>{{ valueB }}</p> <p>{{ valueC }}</p> <!-- valueC变化也会更新 --> </div>
<!-- 列表优化 --> <div v-for="item in list" :key="item.id" v-memo="[item.id, item.status]"> <p>{{ item.content }}</p> </div>
|
28. defineProps和defineEmits是什么?
参考答案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <script setup> // 编译器宏,自动转换为setup函数内容 const props = defineProps({ title: String, count: { type: Number, default: 0 } })
const emit = defineEmits<{ (e: 'update', value: string): void (e: 'delete', id: number): void }>()
// 使用 emit('update', 'new value') </script>
|
29. Vue3中的setup语法糖有哪些?
参考答案:
| 语法糖 |
说明 |
| defineProps |
定义props |
| defineEmits |
定义emit |
| defineExpose |
暴露组件属性 |
| useSlots |
获取slots |
| useAttrs |
获取attrs |
| withDefaults |
props默认值 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <script setup> import { useSlots, useAttrs } from 'vue'
defineProps({ title: String })
defineExpose({ method: () => console.log('exposed') })
const slots = useSlots() const attrs = useAttrs() </script>
|
30. Vue3的TransitionGroup有什么变化?
参考答案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <template> <transition-group name="list"> <div v-for="item in items" :key="item.id"> {{ item.name }} </div> </transition-group> </template>
<style> .list-enter-active, .list-leave-active { transition: all 0.3s; }
.list-enter-from, .list-leave-to { opacity: 0; transform: translateX(30px); }
.list-move { transition: transform 0.3s; } </style>
|
总结
Vue3核心知识图谱
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| ┌─────────────────────────────────────────────────────────────┐ │ Vue3 核心知识图谱 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 基础 │ │ ├── Composition API (setup/ref/reactive/computed) │ │ ├── 响应式API (toRefs/toRaw/isRef/isReactive) │ │ └── 生命周期 (onXxx) │ │ │ │ 组件 │ │ ├── Teleport / Suspense / Fragment │ │ ├── 组件通信 (props/emit/provide/inject) │ │ └── 插槽 (默认/具名/动态) │ │ │ │ 原理 │ │ ├── Proxy响应式原理 │ │ ├── 依赖收集与触发 │ │ ├── Block Tree优化 │ │ └── Tree-shaking支持 │ │ │ │ 生态 │ │ ├── Vue Router 4 │ │ ├── Pinia │ │ ├── Vite │ │ └── Nuxt 3 │ │ │ └─────────────────────────────────────────────────────────────┘
|
面试高频问题TOP10
| 排名 |
问题 |
| 1 |
ref和reactive的区别 |
| 2 |
Vue3响应式原理 |
| 3 |
Composition API优势 |
| 4 |
setup函数执行时机 |
| 5 |
watch和watchEffect |
| 6 |
Vue3生命周期变化 |
| 7 |
Proxy相比Object.defineProperty的优势 |
| 8 |
Teleport和Suspense |
| 9 |
Vue3性能优化 |
| 10 |
逻辑复用方式 |
相关链接