C43. MVVM 设计与实现
3.1. 组合式 API 简化选项式 API 的代码结构和类型推断
组合式 API(Composition API) 是 Vue 3 的核心改进,通过
setup()
函数集中管理响应式数据和逻辑,替代 Vue 2 的选项式 API(如data
,methods
)。
🔄 对比示例:
选项式 API(Vue 2):
vue
<template>
<p>{{ message }}</p>
<button @click="increment">+1</button>
</template>
<script>
export default {
data() {
return {
message: 'Hello Vue!',
count: 0,
};
},
methods: {
increment() {
this.count++;
this.message = `Count is ${this.count}`;
},
},
};
</script>
组合式 API(Vue 3):
vue
<template>
<p>{{ message }}</p>
<button @click="increment">+1</button>
</template>
<script setup>
import { ref } from 'vue';
const message = ref('Hello Vue!');
const count = ref(0);
const increment = () => {
count.value++;
message.value = `Count is ${count.value}`;
};
</script>
🔍 优势:
- 代码结构化:
- 逻辑按功能组织(如
setup()
中定义数据和方法)。 - 避免选项式 API 的“数据分散”问题。
- 逻辑按功能组织(如
- 类型推断:
- 使用 TypeScript 时,无需额外注解即可推断类型。
typescriptconst count: Ref<number> = ref(0); // 自动推断类型
3.2. 响应式的底层是 Proxy 代理
Vue 3 响应式系统 基于 ES6 的 Proxy 对象,替代 Vue 2 的
Object.defineProperty
,实现更全面的拦截和性能优化。
🕶️ 核心原理:
- Proxy 拦截操作:
- 监听
get
、set
等操作,自动触发依赖收集和视图更新。
- 监听
- 数组变化检测:
- 直接支持
push
、splice
等变异方法的拦截,无需Vue.set
。
- 直接支持
🧪 示例代码:
javascript
const originalData = { count: 0 };
const reactiveData = new Proxy(originalData, {
get(target, key) {
console.log(`读取 ${key} 的值`);
return target[key];
},
set(target, key, value) {
console.log(`设置 ${key} 为 ${value}`);
target[key] = value;
// 触发视图更新
notifyViews();
},
});
reactiveData.count++; // 触发 get 和 set
WARNING
- Vue 内部封装:开发者无需直接操作 Proxy,通过
ref
/reactive
使用。 - 兼容性:ES5 环境需 polyfill,但现代浏览器已支持。
3.3. watch 函数侦测响应式数据变化的原理
watch
是 Vue 监听数据变化的核心函数,通过 依赖收集 和 回调触发 实现响应式更新。
🔄 工作流程:
- 依赖收集:
- 在
watch
回调首次执行时,记录依赖的响应式数据。
- 在
- 变化触发:
- 当依赖的数据变化时,执行回调函数。
📝 基础用法:
vue
<script setup>
import { ref, watch } from 'vue';
const count = ref(0);
const message = ref('初始');
// 监听 count 变化
watch(
() => count.value,
(newVal, oldVal) => {
console.log(`count 从 ${oldVal} 变为 ${newVal}`);
message.value = `最新值:${newVal}`;
}
);
</script>
3.4. watch 函数的进阶用法
watch
支持深度监听、立即执行、手动停止等高级功能。
💡 进阶参数:
深度监听(
deep
):javascriptconst user = reactive({ name: '张三', age: 25 }); watch( () => user, (newVal, oldVal) => { // 监听对象所有属性 }, { deep: true } );
立即执行(
immediate
):javascriptwatch( count, () => {}, { immediate: true } // 初始化时立即触发 );
停止监听:
javascriptconst stop = watch(count, () => { /* ... */ }); stop(); // 停止监听
异步更新:
javascriptwatch( searchQuery, () => { // 延迟 500ms 后执行 }, { flush: 'post' } );
3.5. Vue 处理数据变化的底层逻辑
Vue 响应式流程 可分为 依赖收集 和 触发更新 两大阶段。
🔄 流程图:
mermaid
graph TD
A[数据变化] --> B{触发 get/set}
B --> C[Proxy 拦截操作]
C --> D[记录依赖]
D --> E[依赖收集(如 watch 或渲染函数)]
E --> F[异步队列(防抖优化)]
F --> G[执行更新]
G --> H[重新渲染视图]
🔍 关键步骤:
- 依赖收集:
- 在渲染或
watch
执行时,Vue 记录当前访问的响应式数据。
- 在渲染或
- 触发更新:
- 当数据变化时,Vue 将更新任务放入队列,避免重复渲染。
- 高效渲染:
- 通过 虚拟 DOM 对比差异,仅更新必要节点。
知识回顾
- 组合式 API:
- 通过
setup()
集中管理逻辑,提升代码可维护性。 - TypeScript 类型推断简化开发。
- 通过
- 响应式原理:
- Proxy 代理实现全面拦截,支持数组和对象的深层变化。
- watch 机制:
- 监听数据变化,支持深度、异步和手动停止。
- 更新流程:
- 依赖收集 → 异步队列 → 视图更新,确保高效渲染。
课后练习
单选题: Vue 3 中,以下哪项是组合式 API 的核心函数?
- A.
data()
- B.
setup()
- C.
methods
- D.
computed
- A.
填空题: Vue 3 响应式系统的核心是______对象,替代了 Vue 2 的______方法。
代码纠错: 修复以下
watch
的监听问题:javascriptwatch(count, (newVal) => { console.log('count 变化了!'); }); // 假设 count 是 ref(0)
操作题: 使用
watch
监听以下对象的name
属性变化,并在控制台输出新旧值:javascriptconst user = reactive({ name: '张三', age: 25 });
扩展题: 分析以下代码的输出顺序,并解释原因:
javascriptconst count = ref(0); watch(count, () => console.log('watch 触发')); console.log('初始渲染'); count.value = 1;