一、核心知识点(30 分钟吃透)
1. Vue 响应式核心定义
Vue 响应式是「数据驱动视图」的底层机制:当数据(data/props/computed 等)发生变化时,视图会自动、高效更新,无需手动操作 DOM,核心目标是让开发者聚焦数据逻辑,而非 DOM 操作。
2. Vue2 vs Vue3 响应式原理(核心差异)
表格
| 版本 | 核心实现 | 核心 API | 优缺点 |
|---|---|---|---|
| Vue2 | 基于 Object.defineProperty() 劫持对象属性的 getter/setter |
Object.defineProperty() |
✅ 兼容性好;❌ 无法监听数组下标 / 长度变化、无法监听新增 / 删除对象属性 |
| Vue3 | 基于 Proxy 代理整个对象 + Reflect 反射 |
new Proxy(target, handler) |
✅ 监听整个对象、支持数组下标 / 新增属性、拦截操作更全面;❌ 不兼容 IE |
3. 响应式核心流程(Vue2 为例,面试高频)
- 初始化劫持 :Vue 实例化时,遍历 data 中的所有属性,用
Object.defineProperty()重写 getter/setter; - 依赖收集:执行模板渲染时,访问 data 属性触发 getter,将当前组件的 Watcher(观察者)收集到 Dep(依赖管理器)中;
- 派发更新:当修改 data 属性触发 setter 时,Dep 通知所有收集的 Watcher,Watcher 触发组件重新渲染(diff 算法对比虚拟 DOM,更新真实 DOM)。
二、实战练习:手写 Vue 计数器(40 分钟必练)
案例 1:Vue2 版计数器(选项式 API)
html
预览
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Vue2 响应式计数器</title>
<!-- 引入Vue2 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!-- 响应式数据绑定 -->
<h2>当前计数:{{ count }}</h2>
<!-- 绑定事件修改数据,视图自动更新 -->
<button @click="increment">+1</button>
<button @click="decrement">-1</button>
<button @click="reset">重置</button>
</div>
<script>
new Vue({
el: '#app',
// 响应式数据
data() {
return {
count: 0 // 该属性会被Vue劫持getter/setter
};
},
// 方法(修改响应式数据)
methods: {
increment() {
this.count++; // 触发setter,视图自动更新
},
decrement() {
this.count--;
},
reset() {
this.count = 0;
}
}
});
</script>
</body>
</html>
案例 2:Vue3 版计数器(组合式 API)
html
预览
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Vue3 响应式计数器</title>
<!-- 引入Vue3 -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<h2>当前计数:{{ count }}</h2>
<button @click="increment">+1</button>
<button @click="decrement">-1</button>
<button @click="reset">重置</button>
</div>
<script>
const { createApp, ref } = Vue;
createApp({
// 组合式API:setup是入口
setup() {
// 声明响应式数据(基础类型用ref)
const count = ref(0);
// 定义方法
const increment = () => {
count.value++; // ref需通过.value修改值
};
const decrement = () => {
count.value--;
};
const reset = () => {
count.value = 0;
};
// 暴露给模板
return { count, increment, decrement, reset };
}
}).mount('#app');
</script>
</body>
</html>
案例 3:极简模拟 Vue2 响应式(理解核心)
js
// 模拟Dep:依赖管理器,收集Watcher
class Dep {
constructor() {
this.watchers = []; // 存储依赖的Watcher
}
// 收集Watcher
depend() {
if (window.target) {
this.watchers.push(window.target);
}
}
// 通知更新
notify() {
this.watchers.forEach(watcher => watcher.update());
}
}
// 模拟Watcher:观察者,触发更新
class Watcher {
constructor(cb) {
this.cb = cb; // 更新视图的回调
window.target = this; // 标记当前Watcher
this.cb(); // 执行回调,触发getter收集依赖
window.target = null;
}
// 更新视图
update() {
this.cb();
}
}
// 模拟Vue2响应式劫持
function defineReactive(obj, key, value) {
const dep = new Dep(); // 每个属性对应一个Dep
Object.defineProperty(obj, key, {
get() {
dep.depend(); // 收集依赖
return value;
},
set(newVal) {
if (newVal !== value) {
value = newVal;
dep.notify(); // 通知更新
}
}
});
}
// 遍历对象,劫持所有属性
function observe(obj) {
for (let key in obj) {
defineReactive(obj, key, obj[key]);
}
}
// 测试
const data = { count: 0 };
observe(data);
// 创建Watcher,模拟视图更新
new Watcher(() => {
console.log('视图更新:', data.count);
});
data.count++; // 触发setter,输出「视图更新:1」
三、核心面试题 + 标准答案(20 分钟背会)
面试题:Vue 的响应式原理是什么?(Vue2/Vue3 都要答)
标准答案(高级工程师视角,精简且有深度)
1. 核心定义
Vue 响应式是「数据驱动视图」的底层实现,核心逻辑是:劫持数据的读取 / 修改操作,在数据变化时自动触发视图更新,无需手动操作 DOM,是 Vue 区别于原生 JS/jQuery 的核心特性。
2. Vue2 响应式原理
- 核心实现 :基于
Object.defineProperty()劫持对象属性的getter/setter; - 核心流程 :
- 数据劫持 :Vue 实例化时,遍历
data中的所有属性,通过Object.defineProperty()重写getter和setter; - 依赖收集 :渲染模板时访问
data属性,触发getter,将当前组件的Watcher(观察者)收集到Dep(依赖管理器)中; - 派发更新 :修改
data属性触发setter,Dep通知所有收集的Watcher,Watcher触发组件重新渲染(通过虚拟 DOM diff 算法更新真实 DOM);
- 数据劫持 :Vue 实例化时,遍历
- 局限性 :无法监听数组下标 / 长度变化、无法监听对象新增 / 删除属性(需用
Vue.set/this.$set补充)。
3. Vue3 响应式原理(面试加分)
- 核心优化 :抛弃
Object.defineProperty(),改用Proxy代理整个对象 +Reflect反射; - 核心优势 :
- 可监听整个对象(而非单个属性),支持对象新增 / 删除属性;
- 原生支持数组下标、长度变化的监听;
- 拦截操作更全面(支持
get/set/deleteProperty等 13 种操作);
- 兼容处理 :Vue3 提供
ref/reactive等 API 统一响应式声明,ref用于基础类型(通过.value访问),reactive用于引用类型。
4. 实际开发中的应用(体现工程化思维)
- Vue2 中修改数组 / 新增对象属性,需用
this.$set(arr, index, val)/this.$set(obj, key, val); - Vue3 中优先用
ref声明基础类型响应式数据,reactive声明对象 / 数组,避免响应式失效; - 避免直接修改 props 数据(单向数据流),通过
emit通知父组件修改,保证响应式链路完整。
四、错题整理模板(10 分钟完成)
表格
| 错题类型 | 错误代码 / 场景 | 错误原因 | 正确思路 / 知识点 |
|---|---|---|---|
| Vue2 响应式失效 | 直接给对象新增属性 this.obj.newKey = 1 |
未用 Vue.set,新增属性未被劫持 | 用 this.$set(this.obj, 'newKey', 1) |
| Vue3 ref 使用错误 | 模板中写 count.value |
忽略模板中 ref 自动解包特性 | 模板中直接用 {``{ count }},JS 中用 count.value |
| 原理理解偏差 | 认为 Vue2 能监听数组 push | 仅 Vue 对 7 个数组方法做了重写,下标仍不行 | Vue2 重写了 push/pop 等 7 种方法,下标修改仍需 $set |
总结
- 核心考点:Vue2/Vue3 响应式的实现差异、依赖收集 / 派发更新流程、响应式失效的解决方法;
- 实战重点 :掌握
$set(Vue2)、ref/reactive(Vue3)的正确用法,避免响应式失效; - 易错点 :Vue2 新增对象属性、修改数组下标导致响应式失效,Vue3 ref 的
.value使用规则。