Vue2 进阶实战:watch、computed 与生命周期的综合应用
一、核心概念回顾
1. 生命周期钩子
Vue 实例从创建到销毁的完整过程,包含 8 个核心钩子:
- 创建阶段:
beforeCreate→created - 挂载阶段:
beforeMount→mounted - 更新阶段:
beforeUpdate→updated - 销毁阶段:
beforeDestroy→destroyed
2. computed 计算属性
- 用途:基于
data和props计算衍生数据 - 特点:
- 自动缓存,仅当依赖数据变化时重新计算
- 必须返回值,常用于模板渲染
- 示例:
computed: { fullName() { return `${this.firstName} ${this.lastName}`; } }
3. watch 监听器
- 用途:监听
data或props的变化,执行副作用逻辑 - 特点:
- 支持异步操作和复杂逻辑
- 可配置
deep: true(深度监听对象)和immediate: true(立即执行)
- 示例:
watch: { searchQuery(newVal, oldVal) { this.fetchData(newVal); } }
二、综合应用场景:数据仪表盘
场景描述
构建一个实时数据仪表盘组件,需满足以下需求:
- 数据加载:在组件挂载后发起异步请求获取数据
- 数据校验:监听用户输入,限制数值范围(如年龄必须为正数)
- 动态计算:根据原始数据生成统计信息(如数据条数、平均值)
- 资源清理:在组件销毁前取消未完成的网络请求
示例代码
<template>
<div class="dashboard">
<h2>实时数据监控</h2>
<div>
<label>年龄:</label>
<input v-model.number="age" @input="validateAge" />
</div>
<div>
<p>数据条数:{{ dataCount }}</p>
<p>平均值:{{ averageValue }}</p>
</div>
</div>
</template>
new Vue({
el: '#app',
data: {
age: 25, // 用户输入的年龄
rawData: [], // 原始数据
requestId: null // 数据请求标识
},
// 计算属性:处理数据生成统计信息
computed: {
dataCount() {
return this.rawData.length;
},
averageValue() {
return this.rawData.length ?
parseFloat(this.rawData.reduce((sum, num) => sum + num, 0) / this.rawData.length).toFixed(2) :
0;
}
},
// 监听器:校验年龄并限制范围
watch: {
age: {
handler(newVal) {
if (newVal < 0) {
this.age = 0; // 强制修正非法值
}
},
immediate: true // 初始化时立即执行校验
},
rawData: {
handler(newData) {
this.logDataChange(newData);
},
deep: true // 深度监听数组变化
}
},
// 生命周期钩子
created() {
console.log('created: 初始化数据');
this.requestId = this.loadData(); // 发起异步请求
},
mounted() {
console.log('mounted: 渲染完成,可操作DOM');
this.$nextTick(() => {
document.querySelector('.dashboard').style.border = '1px solid #ccc';
});
},
beforeDestroy() {
console.log('beforeDestroy: 清理资源');
if (this.requestId) {
cancelRequest(this.requestId); // 取消未完成的请求
}
},
methods: {
// 模拟异步数据加载
loadData() {
return requestData().then(data => {
this.rawData = data;
}).catch(err => {
console.error('数据加载失败:', err);
});
},
// 年龄校验逻辑
validateAge(event) {
const value = parseInt(event.target.value);
if (isNaN(value) || value < 0) {
alert('年龄必须为非负整数!');
event.target.value = 0; // 强制修正输入框值
}
},
// 监听数据变化的日志记录
logDataChange(data) {
console.log('数据更新:', data);
}
}
});
三、代码执行流程解析
1. 初始化阶段(created)
- 动作:调用
loadData()发起异步请求加载数据 - 关键逻辑:保存请求 ID 以便后续取消
created() {
this.requestId = this.loadData(); // 保存请求标识
}
2. 挂载阶段(mounted)
- 动作:操作 DOM 元素样式(如添加边框)
- 注意:需在
this.$nextTick中执行,确保 DOM 已渲染
mounted() {
this.$nextTick(() => {
document.querySelector('.dashboard').style.border = '1px solid #ccc';
});
}
3. 数据监听(watch)
age监听:immediate: true:初始化时立即校验输入值- 强制修正非法输入(负数或非数字)
watch: {
age: {
handler(newVal) {
if (newVal < 0) {
this.age = 0;
}
},
immediate: true
}
}
rawData监听:deep: true:监听数组内部变化(增删改)- 记录数据变更日志
watch: {
rawData: {
handler(newData) {
this.logDataChange(newData);
},
deep: true
}
}
4. 计算属性(computed)
dataCount:直接返回数组长度,依赖rawDataaverageValue:计算平均值并格式化,依赖rawData
computed: {
dataCount() { return this.rawData.length; },
averageValue() { return parseFloat(this.rawData.reduce((sum, num) => sum + num, 0) / this.rawData.length).toFixed(2); }
}
5. 资源清理(beforeDestroy)
- 动作:取消未完成的异步请求,防止内存泄漏
beforeDestroy() {
if (this.requestId) { cancelRequest(this.requestId); }
}
四、常见问题与最佳实践
1. 为什么在 mounted 中操作 DOM?
- 原因:
mounted时模板已渲染完成,可安全操作真实 DOM。若在created中操作,DOM 尚未挂载,会导致错误。
2. computed 与 watch 的区别
| 特性 | computed | watch |
|---|---|---|
| 触发时机 | 依赖数据变化时自动计算 | 数据变化时手动执行回调 |
| 返回值 | 必须返回值(用于模板渲染) | 无返回值限制 |
| 性能优化 | 自动缓存,避免重复计算 | 需手动优化(如限制监听深度) |
| 适用场景 | 处理派生数据(如过滤、排序) | 执行副作用逻辑(如异步请求) |
3. 避免在 destroyed 中使用 this
- 问题:组件销毁后,
this指向普通对象,无法访问组件实例属性。 - 解决方案:在
beforeDestroy中完成所有清理逻辑。
4. watch 的性能优化
- 深度监听:仅在必要时设置
deep: true,避免性能损耗。 - 立即执行:谨慎使用
immediate: true,防止初始化时重复触发。
五、总结与建议
1. 综合使用原则
- 生命周期:在
created/mounted中初始化数据,在beforeDestroy中清理资源。 - computed:处理需要缓存的派生数据,避免重复计算。
- watch:监听数据变化并执行副作用逻辑(如异步请求、校验)。
2. 调试技巧
- 日志输出:在每个钩子中添加
console.log,观察执行顺序。 - 断点调试:使用浏览器开发者工具设置断点,检查数据流和DOM状态。
3. 进阶扩展
- 结合路由钩子:在
mounted中同步路由参数和组件数据。 - 自定义混入:通过全局混入(Vue.mixin)复用生命周期逻辑。
通过本文的示例和分析,您可以掌握 Vue2 中 watch、computed 和生命周期的核心用法,在实际项目中实现高效、可维护的组件开发。