面试官提问环节
面试官:说说你对 Vue 响应式的理解?
Vue 的响应式系统是其核心特性之一,我主要从以下几个方面来理解:
Vue2 的响应式实现
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
| function defineReactive(obj, key, val) { const dep = new Dep() Object.defineProperty(obj, key, { get() { if (Dep.target) { dep.depend() } return val }, set(newVal) { if (newVal === val) return val = newVal dep.notify() } }) }
1. 对数组的变化监听需要特殊处理 2. 新增、删除属性需要使用 Vue.set/delete 3. 嵌套对象需要递归遍历
|
Vue3 的响应式优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| function reactive(target) { return new Proxy(target, { get(target, key, receiver) { track(target, key) return Reflect.get(target, key, receiver) }, set(target, key, value, receiver) { const result = Reflect.set(target, key, value, receiver) trigger(target, key) return result } }) }
1. 可以监听数组变化 2. 可以监听对象属性的添加和删除 3. 支持 Map、Set、WeakMap、WeakSet 4. 性能更好,不需要递归遍历
|
面试官:项目中遇到过哪些性能问题?如何解决的?
在之前提到的虚拟表格项目中,我遇到了以下性能问题:
1. 大数据渲染问题
1 2 3 4 5 6
| 问题:10万条数据渲染,导致页面卡顿 解决方案: 1. 实现虚拟滚动,只渲染可视区域数据 2. 使用 transform 代替 top 定位 3. 使用 RAF 优化滚动事件 4. 实现缓冲区机制提升滚动体验
|
2. 频繁更新问题
1 2 3 4 5 6
| 问题:实时数据频繁更新导致性能问题 解决方案: 1. 使用 Web Worker 处理数据计算 2. 实现增量更新机制 3. 使用 Object.freeze 冻结不变数据 4. 优化更新频率,合并多次更新
|
3. 内存泄漏问题
1 2 3 4 5 6
| 问题:长时间运行后内存占用过高 解决方案: 1. 及时清理不可见区域的 DOM 2. 解绑事件监听器 3. 清理定时器和订阅 4. 使用 WeakMap/WeakSet 存储引用
|
面试官:你是如何设计一个组件的?
以虚拟表格组件为例,我的设计思路是:
1. 接口设计
1 2 3 4 5 6 7 8 9
| 1. 保持简单直观: - 必要的 props:data、columns、height - 可选的功能 props:selectable、sortable、expandable - 统一的事件命名:onSort、onSelect、onExpand
2. 兼顾灵活性: - 支持自定义列模板 - 支持自定义排序逻辑 - 支持自定义展开行内容
|
2. 性能考虑
1 2 3 4 5 6 7 8 9
| 1. 渲染性能: - 虚拟滚动 - 函数式组件 - 合理的更新粒度
2. 内存优化: - 缓存计算结果 - 及时清理资源 - 优化数据结构
|
3. 可维护性
1 2 3 4 5 6 7 8 9
| 1. 代码组织: - 单一职责 - 逻辑分层 - 清晰的注释
2. 扩展性: - 插件机制 - 钩子函数 - 预留扩展接口
|
这些都是我在实际项目中总结的经验,每个方案都经过了实践验证。
面试官:说说你对 Vue 生命周期的理解?
Vue 的生命周期是组件从创建到销毁的整个过程,我主要从以下几个方面来理解:
Vue2 生命周期
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 1. 创建阶段: beforeCreate: 实例创建前,data/methods 都不可用 created: 实例创建后,可访问 data/methods,但未挂载 DOM 2. 挂载阶段: beforeMount: 模板编译完成,但未挂载到 DOM mounted: DOM 挂载完成,可以访问 DOM 元素 3. 更新阶段: beforeUpdate: 数据更新,但 DOM 未更新 updated: DOM 更新完成 4. 销毁阶段: beforeDestroy: 实例销毁前,可以清理事件、定时器等 destroyed: 实例销毁后,所有指令解绑,子实例销毁
实际应用: - created: 发起数据请求,初始化数据 - mounted: 操作 DOM,初始化第三方库 - beforeDestroy: 清理定时器、取消订阅
|
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 28 29 30
| import { onMounted, onBeforeMount, onBeforeUnmount } from 'vue'
setup() { onBeforeMount(() => { }) onMounted(() => { }) onBeforeUpdate(() => { }) onUpdated(() => { }) onBeforeUnmount(() => { }) onUnmounted(() => { }) }
|
面试官:Vue2 和 Vue3 的区别?
主要从以下几个方面来说:
1. 响应式系统
1 2 3 4 5 6 7 8 9 10
| 1. 需要递归遍历对象 2. 不能监听数组索引和长度变化 3. 不能监听对象属性的添加和删除
1. 可以监听整个对象 2. 可以监听数组变化 3. 支持 Map、Set 等数据结构 4. 性能更好,不需要递归
|
2. 组合式 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 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| export default { data() { return { count: 0 } }, methods: { increment() { this.count++ } }, computed: { doubleCount() { return this.count * 2 } } }
import { ref, computed } from 'vue'
export default { setup() { const count = ref(0) const doubleCount = computed(() => count.value * 2) function increment() { count.value++ } return { count, doubleCount, increment } } }
优势: 1. 更好的代码组织 2. 更好的逻辑复用 3. 更好的类型推导
|
3. 性能优化
1 2 3 4 5 6 7 8 9 10 11 12 13
| 1. 编译优化: - 静态节点提升 - Patch Flag 标记 - 块级树结构 2. 按需编译: - Tree-shaking 支持 - 更小的打包体积 3. 新特性: - Fragment - Teleport - Suspense
|
面试官:Vue 中如何实现组件通信?
我通常使用以下几种方式:
1. Props/Emit
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
| // 父组件 <template> <child-component :message="message" @update="handleUpdate" /> </template>
// 子组件 <template> <div @click="handleClick">{{ message }}</div> </template>
<script> export default { props: ['message'], methods: { handleClick() { this.$emit('update', 'new value') } } } </script>
适用场景:父子组件通信
|
2. Provide/Inject
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| export default { provide() { return { theme: this.theme, updateTheme: this.updateTheme } } }
export default { inject: ['theme', 'updateTheme'] }
适用场景:跨层级组件通信
|
3. Vuex/Pinia
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
| const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment(state) { state.count++ } } })
const useStore = defineStore('main', { state: () => ({ count: 0 }), actions: { increment() { this.count++ } } })
适用场景:全局状态管理
|
4. EventBus
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const bus = new Vue()
bus.$emit('update', data)
bus.$on('update', data => {})
const emitter = mitt()
emitter.emit('update', data)
emitter.on('update', data => {})
适用场景:非父子组件通信 注意事项:需要及时销毁监听器
|
这些都是我在实际项目中经常使用的通信方式,选择哪种方式主要取决于:
- 组件间的关系(父子、兄弟、跨层级)
- 通信的频率和复杂度
- 是否需要状态管理
- 是否需要调试工具支持