前言
昨天一个读者发消息:拿到offer了,就是因为把响应式原理答透了,面试官当场说"基础很扎实",薪资从12K直接跳到15K。
响应式原理是Vue面试的分水岭。答不好,顶多拿个junior offer;答透了,薪资直接往上跳一档。
很多人觉得原理题难,其实只是没人告诉你面试官真正想听什么。今天这8题,我会拆解面试官的考察逻辑,告诉你什么回答能让他打高分。
欢迎阅读我的Vue专栏文章
Vue Router这8题:80%的人挂在"讲讲你的路由设计"
Vuex面试7题:你以为的"会用",在面试官眼里都是"不懂原理"
23. Vue2响应式系统的实现原理?Object.defineProperty的作用?
速记公式:劫持getter/setter,依赖收集,派发更新,数组特殊处理
标准答案
Vue2响应式系统基于Object.defineProperty实现,通过劫持对象属性的getter/setter来监听数据变化。
核心流程:
当组件初始化时,Vue会遍历data对象的所有属性,使用Object.defineProperty为每个属性定义getter和setter。在getter中进行依赖收集,记录哪些组件使用了这个数据;在setter中触发更新,通知相关组件重新渲染。
具体工作机制:每个响应式属性都有一个Dep依赖收集器,存储所有依赖这个属性的Watcher。当你读取属性时(比如模板渲染、computed计算),当前的Watcher会被添加到这个属性的Dep中。当你修改属性时,setter触发,Dep通知所有收集的Watcher执行更新。
局限性: Object.defineProperty只能劫持已知属性,无法监听数组索引变化和对象新增属性。所以Vue2对数组做了特殊处理,重写了push、pop、shift等7个变异方法。对象新增属性需要用Vue.set()或this.$set()才能变成响应式。
面试官真正想听什么
这题考察你对Vue底层实现的理解深度。只说"Object.defineProperty监听数据变化"是不够的,面试官想知道你理不理解依赖收集和派发更新的完整流程。
加分回答
Vue2响应式的核心是数据劫持+观察者模式。我用一个具体例子解释:
// 简化版实现
function defineReactive(obj, key, val) {
const dep = new Dep() // 每个属性都有自己的依赖收集器
Object.defineProperty(obj, key, {
get() {
// 依赖收集:把当前Watcher加入dep
if (Dep.target) {
dep.depend()
}
return val
},
set(newVal) {
if (newVal === val) return
val = newVal
// 派发更新:通知所有Watcher
dep.notify()
}
})
}
我在项目中踩过坑:
做用户管理时,从接口拿到用户列表后,想给每个用户动态添加一个selected属性:
this.users.forEach(user => {
user.selected = false // 这样加的属性不是响应式的
})
结果点击checkbox,selected改了但视图不更新。后来改成:
this.users.forEach(user => {
this.$set(user, 'selected', false) // 用$set才能变成响应式
})
这个坑让我深刻理解了Object.defineProperty的局限:只能劫持初始化时就存在的属性。
数组的处理也很巧妙: Vue重写了数组的7个方法(push/pop/shift/unshift/splice/sort/reverse),在这些方法里手动触发更新。所以this.list.push(item)能触发更新,但this.list[0] = item不行。"
减分回答
❌ "Object.defineProperty监听数据变化"(只说了表面)
❌ 说不出依赖收集和派发更新的流程(不理解核心机制)
❌ 不知道为什么数组索引修改不响应(没踩过坑)
24. Vue中依赖收集和派发更新的过程?Watcher的作用?
速记公式:getter收集,setter通知,Watcher桥梁,三种类型
标准答案
依赖收集和派发更新是响应式系统的核心流程。
依赖收集过程:
当组件渲染、computed计算或watch监听时,会触发数据的getter。此时Dep依赖收集器会将当前活跃的Watcher添加到依赖列表中。比如模板中用了{{message}},渲染时会读取message属性,message的Dep就会收集这个渲染Watcher。
派发更新过程:
当数据发生变化时,setter被触发,Dep会通知所有收集到的Watcher执行更新。Watcher收到通知后会被推入更新队列,Vue会在下一个tick统一执行这些更新,避免重复渲染。
Watcher是响应式系统的桥梁,连接数据和视图。它有三种类型:
- 渲染Watcher:负责组件重新渲染
- 计算Watcher:处理computed属性的缓存和更新
- 用户Watcher:处理watch选项的回调
每个Watcher都有update方法,当依赖的数据变化时会被调用。Vue的异步更新队列确保每个组件在同一个事件循环中只更新一次。
面试官真正想听什么
这题考察你对响应式系统数据流的理解。能说清楚这个流程,说明你真的理解Vue是怎么工作的,不只是会用API。
加分回答
我用一个完整的例子说明这个流程:
<template>
<div>{{ fullName }}</div>
</template>
<script>
export default {
data() {
return {
firstName: '张',
lastName: '三'
}
},
computed: {
fullName() {
return this.firstName + this.lastName
}
}
}
</script>
完整流程:
- 组件渲染时:模板需要fullName,触发computed的getter
- computed计算时:读取firstName和lastName,触发它们的getter
- 依赖收集:firstName和lastName的Dep分别收集了fullName的计算Watcher
- 修改数据:执行
this.firstName = '李',触发firstName的setter - 派发更新:firstName的Dep通知计算Watcher,计算Watcher通知渲染Watcher
- 异步更新:渲染Watcher进入队列,下一个tick执行,组件重新渲染
关键点:
- 一个数据可以被多个Watcher依赖:firstName同时被computed和模板依赖
- 一个Watcher可以依赖多个数据:fullName依赖firstName和lastName
- 更新是异步批量的:连续改10次firstName,只会触发1次渲染
我在项目中遇到过性能问题:
// 不好的写法
for (let i = 0; i < 1000; i++) {
this.count++ // 每次都触发setter
}
虽然Vue有异步更新队列,但频繁触发setter还是有开销。改成:
const result = this.count + 1000
this.count = result // 只触发一次setter
这让我理解了:虽然Vue做了优化,但写代码时还是要注意减少不必要的响应式触发。"
减分回答
❌ 说不清楚依赖收集的时机(概念模糊)
❌ 不知道Watcher有三种类型(基础不扎实)
❌ 不理解异步更新队列的作用(缺少深度)
25. Vue响应式系统的局限性?哪些操作无法被检测到?
速记公式:数组索引,新增属性,深层嵌套,需用$set
标准答案
Vue2响应式系统有几个检测盲区,都源于Object.defineProperty的限制。
1. 数组索引直接赋值和length修改:
this.items[0] = newValue // 无法检测
this.items.length = 0 // 无法检测
Object.defineProperty无法拦截数组的这些操作。正确做法: 用this.$set(this.items, 0, newValue)或数组的变异方法splice。
2. 对象属性的动态添加和删除:
this.user.newProp = 'value' // 无法检测
delete this.user.oldProp // 无法检测
因为defineProperty只劫持初始化时存在的属性。正确做法: 用this.$set(this.user, 'newProp', 'value')和this.$delete(this.user, 'oldProp')。
3. 嵌套对象的深层变化:
虽然Vue会递归处理嵌套对象,但如果对象嵌套太深,或者某个属性初始值是null/undefined,后续赋值为对象时,这个对象不会自动变成响应式。
4. 直接替换整个对象的引用:
this.user = { name: 'new' } // 可以检测
const obj = this.user
obj.name = 'changed' // 可以检测
this.user = obj // 虽然能检测,但如果obj来自外部且未经处理,可能丢失响应式
Vue3用Proxy彻底解决了这些问题。 Proxy能拦截对象的所有操作,包括属性添加、删除、数组索引操作等,让响应式系统更完善。
面试官真正想听什么
这题考察你对Vue局限性的认知和实际踩坑经验。只会用不知道坑的人,说明项目经验不够深。
加分回答
我在项目中把这几个坑都踩过:
坑1:动态添加属性不响应
做表格多选功能时,给每行数据加selected属性:
// 错误写法
this.tableData.forEach(row => {
row.selected = false
})
// checkbox改了selected,视图不更新
// 正确写法
this.tableData = this.tableData.map(row => ({
...row,
selected: false
}))
// 或者用$set,但批量操作map更高效
坑2:数组索引赋值不响应
修改购物车商品数量:
// 错误写法
this.cartItems[index].count = newCount
// 正确写法
this.$set(this.cartItems[index], 'count', newCount)
// 或者
this.cartItems[index] = { ...this.cartItems[index], count: newCount }
坑3:清空数组不响应
// 错误写法
this.list.length = 0
// 正确写法
this.list.splice(0, this.list.length)
// 或者
this.list = []
这些坑让我养成习惯:
- 需要动态属性的对象,初始化时就定义好,值设为null
- 数组操作优先用变异方法(push/splice等)
- 实在要直接赋值,就用$set
Vue3用Proxy就没这些问题了:
// Vue3中完全OK
this.items[0] = newValue
this.user.newProp = 'value'
this.list.length = 0
这也是为什么Vue3的开发体验比Vue2好很多。
减分回答
❌ 只知道"数组索引不响应",说不出其他盲区(不全面)
❌ 不知道怎么解决这些问题(只知道问题不知道方案)
❌ 不知道Vue3改进了什么(没关注版本演进)
26. Vue3响应式系统相比Vue2有什么改进?Proxy的优势?
速记公式:Proxy拦截全,性能更优,支持更多操作,惰性响应
标准答案
Vue3用Proxy重写了响应式系统,带来了显著改进。
1. Proxy能拦截对象的所有操作:
包括属性访问、赋值、枚举、删除、函数调用等,而Object.defineProperty只能劫持get/set。这意味着Vue3可以直接检测数组索引变更、length修改、动态新增属性,不需要$set。
2. 性能优势:
Vue3实现了惰性响应式,只有被访问的属性才会被代理。Vue2需要在初始化时递归遍历所有属性,对于深层嵌套对象,Vue3按需代理子对象,显著减少了初始化开销。
3. 支持更丰富的拦截操作:
Proxy支持has(in操作符)、deleteProperty(delete操作)、ownKeys(Object.keys)等拦截,让Vue3能检测更多类型的操作。
4. 与Reflect配合使用:
Proxy配合Reflect API,保证了操作的正确性和一致性。Reflect提供了对象操作的默认行为,让代理逻辑更可靠。
5. 响应式系统解耦:
Vue3的响应式系统独立于组件,可以在组件外部使用。这让ref、reactive等API能够独立工作,为Composition API提供了基础支撑。
6. 更好的TypeScript支持:
Proxy的类型推导比defineProperty准确,配合Composition API,Vue3的类型提示更完善。
面试官真正想听什么
这题考察你对技术演进的理解和Vue3新特性的掌握。还停留在Vue2思维的人,在2025年的面试中会失去竞争力。
加分回答
我在Vue2项目中遇到的很多问题,Vue3都解决了:
问题1:动态属性需要$set
Vue2中:
// 麻烦
this.$set(this.user, 'age', 18)
Vue3中:
// 直接写
this.user.age = 18
问题2:数组操作受限
Vue2中:
// 不响应
this.list[0] = newItem
this.list.length = 0
// 要这样
this.$set(this.list, 0, newItem)
this.list.splice(0, this.list.length)
Vue3中:
// 都能响应
this.list[0] = newItem
this.list.length = 0
性能提升实测:
我把一个复杂的数据看板从Vue2迁移到Vue3,数据结构有10层嵌套,1000+个响应式属性。
- Vue2初始化:遍历所有属性,耗时280ms
- Vue3初始化:惰性代理,只处理第一层,耗时85ms
- 性能提升70%
而且Vue3在渲染和更新方面也快了很多:
- 初始渲染快41%
- 更新快56%
- 内存减少54%
Proxy让代码写起来更自然: 不用记$set、不用记哪些操作响应哪些不响应,就按JavaScript原生方式写,Vue3都能正确响应。
这就是为什么现在新项目都推荐用Vue3。
减分回答
❌ 只说"Proxy比defineProperty好"(太空泛)
❌ 说不出具体的性能数据(缺少实测)
❌ 不知道Vue3还有哪些其他改进(只知道Proxy这一点)
27. Vue中nextTick的实现原理?为什么需要异步更新?
速记公式:微任务队列,批量更新,DOM更新后执行,性能优化
标准答案
nextTick基于事件循环机制实现异步执行,核心原理是将回调函数推入微任务或宏任务队列,确保在DOM更新完成后执行。
为什么需要异步更新:
如果每次数据变化都立即同步更新DOM,会造成频繁的重排重绘,性能极差。比如在一个函数中连续修改10次同一个数据,同步更新会触发10次DOM操作,而异步更新只会在下一个tick执行一次最终结果的更新。
实现机制:
Vue的nextTick会优先选择微任务,降级策略是:Promise.resolve() -> MutationObserver -> setImmediate -> setTimeout。当你调用nextTick时,Vue会将回调推入队列,等待当前执行栈清空后,在下一个事件循环周期执行。
典型使用场景:
当你修改数据后需要立即获取更新后的DOM元素尺寸或进行DOM操作时,必须使用nextTick:
this.showModal = true
this.$nextTick(() => {
// 此时DOM已更新,可以安全操作
this.$refs.modalInput.focus()
})
Vue3中的nextTick基于Promise实现,返回Promise对象,可以使用async/await语法,让异步代码更优雅。
面试官真正想听什么
这题考察你对异步机制和性能优化的理解。nextTick是Vue核心机制,理解它说明你理解Vue的更新策略。
加分回答
我用一个具体例子说明为什么需要异步更新:
// 假设同步更新
methods: {
updateUser() {
this.user.name = '张三' // 触发渲染
this.user.age = 18 // 触发渲染
this.user.address = '北京' // 触发渲染
// 触发3次DOM更新,性能差
}
}
// Vue的异步更新
methods: {
updateUser() {
this.user.name = '张三' // 标记需要更新
this.user.age = 18 // 标记需要更新
this.user.address = '北京' // 标记需要更新
// 下一个tick统一更新一次DOM
}
}
我在项目中的实际应用:
场景1:操作DOM元素
做一个可编辑的表格,双击单元格变成输入框并自动聚焦:
handleCellClick(row) {
row.editing = true
// 错误:此时DOM还没更新,$refs.input不存在
// this.$refs.input.focus()
// 正确:等DOM更新后再操作
this.$nextTick(() => {
this.$refs.input.focus()
})
}
场景2:获取DOM尺寸
动态添加内容后计算容器高度:
addContent() {
this.content = '很长的文本...'
this.$nextTick(() => {
const height = this.$refs.container.offsetHeight
console.log('内容高度:', height)
})
}
场景3:Echarts图表重绘
updateChartData() {
this.chartData = newData
this.$nextTick(() => {
// DOM更新完成,图表容器尺寸已变化
this.$refs.chart.resize()
})
}
Vue3的用法更简洁:
await nextTick()
this.$refs.input.focus()
核心理解:Vue的数据是同步改的,但DOM是异步更新的。nextTick就是连接这两者的桥梁。
减分回答
❌ 只说"nextTick在DOM更新后执行"(不理解为什么)
❌ 不知道微任务和宏任务的区别(事件循环基础不扎实)
❌ 说不出实际使用场景(理论派)
28. Vue响应式数据的深度监听如何实现?deep选项的作用?
速记公式:递归遍历,deep开启,性能开销,谨慎使用
标准答案
Vue的响应式系统默认只监听对象的第一层属性。 当你修改嵌套对象的深层属性时,如果没有特殊处理,Vue无法检测到变化。
deep选项的作用是启用深度监听,让Vue递归遍历对象的所有嵌套属性,为每个属性都建立响应式连接。
在watch中使用deep:
watch: {
userInfo: {
handler(newVal, oldVal) {
console.log('深层属性变化了')
},
deep: true
}
}
开启deep后,修改userInfo.address.city这样的深层属性也会触发监听器。
性能考虑:
深度监听会带来性能开销,因为Vue需要遍历对象的所有属性建立响应式连接。对于大型对象,这个开销可能很明显。
Vue3的改进:
Vue3的Proxy让深度响应式变得更高效,但仍然要根据实际需求谨慎使用deep选项。如果只需要监听特定属性,直接监听那个属性路径更好:
watch: {
'userInfo.address.city'(newVal) {
// 只监听city属性,性能更好
}
}
面试官真正想听什么
这题考察你对性能优化的认知。滥用deep的人,说明不考虑性能影响。
加分回答
"我在项目中对deep选项的使用踩过坑:
反面案例:滥用deep
做表单编辑时,监听整个表单对象的变化:
watch: {
formData: {
handler() {
this.hasChanged = true
},
deep: true
}
}
表单有30个字段,每个字段改动都会触发deep遍历整个对象,输入框输入时明显卡顿。
优化方案1:只监听需要的字段
watch: {
'formData.username'() {
this.validateUsername()
},
'formData.email'() {
this.validateEmail()
}
}
优化方案2:用computed
如果只是判断是否有修改,用computed更高效:
computed: {
hasChanged() {
return JSON.stringify(this.formData) !== this.originalData
}
}
什么时候必须用deep:
- 监听数组的深层变化:数组里的对象属性改变
- 不确定哪个属性会变:配置对象、动态表单
- 对象结构复杂:嵌套3层以上
性能优化建议:
- 能用computed就不用watch
- 能监听具体路径就不用deep
- 必须用deep时,在handler里做防抖
watch: {
config: {
handler: _.debounce(function() {
this.saveConfig()
}, 300),
deep: true
}
}
Vue3用reactive + watch,默认就是深度监听:
const state = reactive({ user: { name: 'Tom' } })
watch(state, () => {
// 自动深度监听
})
但要注意,不是所有场景都需要深度监听,要根据实际需求权衡性能和便利性。"
减分回答
❌ 不知道deep有性能开销(没有性能意识)
❌ 所有对象都加deep(过度使用)
❌ 不知道可以监听具体属性路径(API不熟)
29. Vue中如何监听数组变化?数组方法的重写原理?
速记公式:重写7个方法,拦截器模式,手动触发,索引直接赋值不响应
标准答案
Vue2中数组变化监听通过重写数组的变异方法实现。 由于Object.defineProperty无法直接监听数组索引变化,Vue采用了拦截器模式。
实现原理:
Vue创建了一个继承自Array.prototype的新对象,然后重写了7个会改变原数组的方法:push、pop、shift、unshift、splice、sort、reverse。当你调用这些方法时,Vue会:
- 先执行原生的数组操作
- 然后手动触发依赖更新
- 对新增的元素递归添加响应式处理
具体实现(简化版):
const arrayProto = Array.prototype
const arrayMethods = Object.create(arrayProto)
['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(method => {
arrayMethods[method] = function(...args) {
// 先调用原生方法
const result = arrayProto[method].apply(this, args)
// 获取响应式处理器
const ob = this.__ob__
// 对新增的元素进行响应式处理
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
if (inserted) ob.observeArray(inserted)
// 手动触发更新
ob.dep.notify()
return result
}
})
限制:
- 直接通过索引修改数组项(
arr[0] = newValue)无法监听 - 修改数组长度(
arr.length = 0)无法监听
需要用this.$set(arr, index, value)或splice方法替代。
Vue3用Proxy彻底解决了这个问题,可以直接监听数组的所有变化操作。
面试官真正想听什么
这题考察你对Vue hack技巧的理解。数组监听是Vue2的经典实现,理解这个说明你对框架源码有研究。
加分回答
Vue对数组的处理很巧妙,我通过源码和实践理解了这个机制:
为什么不用defineProperty监听数组:
技术上可以给数组的每个索引定义getter/setter,但性能开销太大。数组可能有成千上万个元素,每个都定义响应式得不偿失。而且数组操作频繁,监听索引意义不大。
重写方法的好处:
数组的修改90%都通过push/splice这些方法,重写7个方法就能覆盖大部分场景,性能和开发体验的平衡点。
我在项目中的实践:
能响应的操作:
this.list.push(item) // ✅ 能响应
this.list.pop() // ✅ 能响应
this.list.splice(0, 1) // ✅ 能响应
this.list.sort() // ✅ 能响应
不能响应的操作:
this.list[0] = newItem // ❌ 不响应
this.list.length = 0 // ❌ 不响应
// 要改成
this.$set(this.list, 0, newItem) // ✅ 能响应
this.list.splice(0) // ✅ 能响应
我踩过的坑:
做商品列表,点击删除按钮:
// 错误写法
deleteItem(index) {
delete this.list[index] // 数组有空洞,而且不响应
}
// 正确写法
deleteItem(index) {
this.list.splice(index, 1) // splice能触发更新
}
清空数组的最佳实践:
// 方式1:重新赋值
this.list = []
// 方式2:splice
this.list.splice(0, this.list.length)
// 不要用length
this.list.length = 0 // 不响应
Vue3的Proxy完美解决:
// Vue3中全部能响应
this.list[0] = newItem
this.list.length = 0
this.list[100] = item // 甚至超出范围的索引也能响应
这个实现让我深刻理解:框架设计要在性能、开发体验、实现复杂度之间找平衡。
减分回答
❌ 只说"Vue重写了数组方法"(不理解为什么)
❌ 不知道哪些操作能响应哪些不能(没踩过坑)
❌ 不知道Vue3改进了什么(没关注演进)
30. Vue响应式系统中的循环依赖问题如何解决?
速记公式:依赖栈管理,ID去重,检测循环,避免死锁
标准答案
Vue通过栈结构和去重机制解决循环依赖问题。
什么是循环依赖:
当computed属性A依赖computed属性B,而B又依赖A时,就形成了循环依赖。如果处理不当,会导致无限递归和栈溢出。
Vue的解决方案:
Vue在依赖收集过程中维护一个Dep.target栈来追踪当前正在执行的Watcher。使用pushTarget和popTarget方法管理依赖收集栈。
具体机制:
- 当A开始计算时,把A的Watcher推入栈顶
- 如果A依赖B,B计算时也把B的Watcher推入栈顶
- 如果B尝试依赖A,Vue检测到A已经在栈中,避免无限递归
- 每个Watcher都有唯一ID,通过Set数据结构去重,确保同一依赖不会重复建立
异步更新队列:
Vue的异步更新机制也帮助避免循环依赖问题。Watcher被推入更新队列时会去重,即使存在循环依赖,也会在队列中被合并,只执行一次更新。
Vue3的effect栈:
Vue3通过effect栈和stop标记进一步优化了这个机制,能更精确地控制响应式依赖的收集和清理。
面试官真正想听什么
这题考察你对响应式系统深层机制的理解。循环依赖是高级话题,能答好说明你真的研究过源码。
加分回答
"我用一个具体例子说明循环依赖和Vue的处理:
潜在的循环依赖场景:
computed: {
displayValue() {
// 依赖formattedValue
return this.formattedValue.toUpperCase()
},
formattedValue() {
// 如果这里不小心访问了displayValue,就形成循环
return this.rawValue.trim()
}
}
Vue的防护机制:
// 简化的依赖收集逻辑
class Dep {
static target = null
static targetStack = []
depend() {
if (Dep.target) {
// 检查Watcher是否已在栈中
if (!this.subs.has(Dep.target.id)) {
this.subs.add(Dep.target.id)
Dep.target.addDep(this)
}
}
}
}
function pushTarget(watcher) {
Dep.targetStack.push(watcher)
Dep.target = watcher
}
function popTarget() {
Dep.targetStack.pop()
Dep.target = Dep.targetStack[Dep.targetStack.length - 1]
}
我在项目中遇到的类似问题:
做表单验证时,错误写了循环引用:
computed: {
isValid() {
return !this.hasErrors // 依赖hasErrors
},
hasErrors() {
// 错误:又访问了isValid
if (!this.isValid) return false
return this.errors.length > 0
}
}
Vue检测到循环依赖,在开发环境报错:[Vue warn]: Maximum recursive updates exceeded
正确的写法:
computed: {
isValid() {
return this.errors.length === 0
},
hasErrors() {
return this.errors.length > 0
}
}
关键理解:
- 依赖关系应该是单向的:A依赖B,B不能依赖A
- 用数据而不是计算属性作为依赖源:减少复杂依赖链
- Vue的保护机制能避免死循环:但不代表你可以随意写循环依赖
这让我明白:响应式系统虽然强大,但开发者要保持清晰的数据流向。"
减分回答
❌ 不知道什么是循环依赖(基础概念不清)
❌ 说不出Vue如何解决(不理解机制)
❌ 没遇到过相关问题(项目经验浅)
总结
这8道响应式原理题,是Vue面试的核心战场。答好这部分,面试官会给你打上'基础扎实、理解底层'的标签,薪资能往上跳一档。
每道题的核心不是背原理,而是:
- 理解为什么这样设计(设计思想)
- 知道有什么局限性(边界认知)
- 遇到过什么相关的坑、怎么解决的(实战经验)
- Vue3改进了什么(技术演进)
高频挂科点:
- 只知道Object.defineProperty劫持数据,说不出依赖收集和派发更新流程
- 不知道Vue2的局限性,没踩过数组索引、动态属性的坑
- 不理解为什么需要异步更新,说不出nextTick的实际应用场景
- 还停留在Vue2思维,不知道Vue3用Proxy改进了什么
- 不理解deep的性能开销,所有watch都加deep
面试加分技巧:
- 答原理时带上代码示例:不要只说概念,写简化版实现
- 对比Vue2和Vue3:展现你对技术演进的关注
- 说出踩过的坑:证明你有实战经验,不是理论派
- 提到性能考虑:说明你有优化意识
接下来该做什么:
- 动手实现一个简易响应式系统:理解依赖收集和派发更新
- 对比Vue2和Vue3项目:体验Proxy带来的改进
- 检查项目代码:看有没有滥用deep、没用nextTick的地方
下一篇我会讲Vue Router路由管理的8道题:hash模式vs history模式、导航守卫、懒加载、权限控制...
最近好多同学挂在 HR 面而不知道为什么,这些问题你都会吗:
- 最后,你还有什么想了解的或者想说的吗?
- 为什么想要加入我们公司,你对我们公司有什么了解?
- 说说你对未来3-5年的职业规划?
- 如果项目紧急需要加班,你能接受吗_怎么看待加班?
没有答题思路? 快来牛面题库看看吧,这是我们共同打造的面试学习一站式平台,拥有丰富的免费题库资源,AI模拟面试等等功能,加入我们,早日斩获Offer吧。
留言区互动:
响应式原理这8题里,你觉得最难的是哪一道?或者你在项目中遇到过什么响应式相关的诡异bug?
评论区说说,点赞最高的问题我会专门分析根因和解决方案。