崩溃!改了十遍还是错?4 组 Vue 技巧让你秒变大神
凌晨一点,办公室只剩键盘敲击声和空调的嗡鸣。盯着屏幕上反复报错的 Vue 项目,咖啡凉了又热,改完的 Bug 却像打地鼠一样冒出来 —— 这是不是你无数个加班夜的缩影?别担心!今天分享 4 组超实用的 Vue2 和 Vue3 实战技巧,专治各种开发 “疑难杂症”,让你下次遇到问题直接 “拿捏”,秒变团队大神!
一、数据响应式 “玄学”:Vue2 vs Vue3 大不同
写代码时最崩溃的莫过于数据改了,页面却没反应!明明按照文档写的,为啥就是不更新?这背后的 “玄学”,在 Vue2 和 Vue3 里答案可不一样!
Vue2 的 “老派功夫” Object.defineProperty
// 定义一个普通对象
const user = {
name: '小明',
age: 25
};
// 手动将对象转为响应式(简化版)
function observe(obj) {
Object.keys(obj).forEach(key => {
let value = obj[key];
// 通过Object.defineProperty劫持对象属性的读写操作
Object.defineProperty(obj, key, {
get() {
console.log(`获取 ${key} 的值`);
return value;
},
set(newValue) {
console.log(`设置 ${key} 的值为 ${newValue}`);
value = newValue;
// 这里应该触发视图更新,实际Vue2的实现更复杂
}
});
});
return obj;
}
const reactiveUser = observe(user);
reactiveUser.age = 26; // 触发视图更新
Vue2 靠Object.defineProperty实现响应式,但它有个 “致命弱点”:新增属性不会自动响应!必须用Vue.set或this.$set手动处理,不然改了数据页面也不会动。
悬念问题:如果项目里有大量动态添加属性的场景,Vue2 怎么才能优雅解决?
Vue3 的 “黑科技” Proxy
import { reactive } from 'vue';
// 创建响应式对象
const state = reactive({
count: 0,
list: []
});
// 直接新增属性,自动变为响应式
state.newProp = '新属性';
// 修改深层属性也能触发更新
state.list.push({ item: '新元素' });
Vue3 用Proxy代替了Object.defineProperty,不仅能监听到新增属性,连深层对象的变化都能捕捉到!不过,遇到循环引用的对象时,Proxy也可能 “翻车”,需要小心处理。
二、组件通信 “迷宫”:两代 Vue 的破局之道
开发时最头疼的就是组件间传数据!父组件传的值,子组件收不到;子组件改了数据,父组件没反应,简直像走进迷宫!
Vue2 的 “传统暗号” props 与 $emit
<!-- 父组件 -->
<template>
<div>
<!-- 通过props传递message给子组件 -->
<ChildComponent :message="parentMessage" @childEvent="handleChildEvent"></ChildComponent>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
data() {
return {
parentMessage: '来自父组件的消息'
};
},
methods: {
handleChildEvent(data) {
// 处理子组件传递过来的数据
console.log('收到子组件消息:', data);
}
}
};
</script>
<!-- 子组件 -->
<template>
<button @click="sendMessage">发送消息</button>
</template>
<script>
export default {
props: {
message: String
},
methods: {
sendMessage() {
// 通过$emit触发childEvent事件,并传递数据
this.$emit('childEvent', '来自子组件的回复');
}
}
};
</script>
在 Vue2 里,父传子用props,子传父靠$emit。但遇到多层嵌套组件,数据传递就像 “俄罗斯套娃”,层层传递特别麻烦!
悬念问题:当组件层级很深时,Vue2 有没有更简单的传值方法?
Vue3 的 “新通信协议” emits 与 defineEmits
<!-- 父组件 -->
<template>
<div>
<ChildComponent :message="parentMessage" @childEvent="handleChildEvent"></ChildComponent>
</div>
</template>
<script>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
setup() {
const parentMessage = ref('父组件消息');
const handleChildEvent = (data) => {
console.log('收到子组件消息:', data);
};
return {
parentMessage,
handleChildEvent
};
}
};
</script>
<!-- 子组件 -->
<template>
<button @click="sendMessage">发送消息</button>
</template>
<script>
import { defineEmits } from 'vue';
export default {
setup() {
// 定义组件触发的事件
const emits = defineEmits(['childEvent']);
const sendMessage = () => {
emits('childEvent', '子组件回复');
};
return {
sendMessage
};
}
};
</script>
Vue3 的defineEmits让事件定义更清晰,配合 Composition API,代码简洁又直观!跨层级传值时,还能搭配provide和inject,轻松打破 “组件壁垒”。
三、计算属性 “陷阱”:小心别掉坑里!
计算属性用起来方便,但稍不注意就会踩坑!数据更新了,计算结果却没变化;逻辑写复杂点,页面直接卡死……
Vue2 的计算属性
export default {
data() {
return {
a: 1,
b: 2
};
},
computed: {
sum() {
// 计算属性会缓存结果,只有依赖的数据变化时才重新计算
return this.a + this.b;
}
}
};
在 Vue2 里,计算属性依赖data中的数据,只要依赖的数据不变,计算属性就直接返回缓存结果,性能杠杠的!但如果在计算属性里修改其他数据,就会打破单向数据流,引发各种诡异问题。
悬念问题:当计算属性依赖异步数据时,Vue2 怎么保证结果正确?
Vue3 的计算属性
import { ref, computed } from 'vue';
export default {
setup() {
const a = ref(1);
const b = ref(2);
// 定义计算属性
const sum = computed(() => {
return a.value + b.value;
});
return {
a,
b,
sum
};
}
};
Vue3 的计算属性在 Composition API 中使用更灵活,但同样要注意依赖关系。一旦依赖的数据变了,计算属性就会自动重新计算,所以千万别在里面写副作用代码!
四、生命周期 “迷魂阵”:找准时机很重要!
组件什么时候创建?什么时候销毁?钩子函数用错地方,代码直接 “原地爆炸”!
Vue2 的生命周期钩子
export default {
data() {
return {
message: 'Hello Vue2'
};
},
beforeCreate() {
console.log('实例刚被创建,data和methods还未初始化');
},
created() {
console.log('实例创建完成,可进行数据请求');
},
beforeMount() {
console.log('模板编译完成,即将挂载到DOM');
},
mounted() {
console.log('组件已挂载到DOM,可操作DOM元素');
},
beforeUpdate() {
console.log('数据更新前,虚拟DOM重新渲染前');
},
updated() {
console.log('数据更新后,虚拟DOM重新渲染完成');
},
beforeDestroy() {
console.log('组件销毁前,可进行资源清理');
},
destroyed() {
console.log('组件已销毁,绑定事件、定时器等已移除');
}
};
Vue2 的生命周期钩子像一套 “组合拳”,每个阶段都有对应的回调函数,只要按需求在合适的钩子函数里写代码就行。
悬念问题:在 Vue2 里,如果在mounted钩子函数里发起多个异步请求,怎么保证数据按顺序渲染?
Vue3 的生命周期钩子
import { onMounted, onUpdated, onUnmounted } from 'vue';
export default {
setup() {
onMounted(() => {
console.log('组件已挂载到DOM');
});
onUpdated(() => {
console.log('组件已更新');
});
onUnmounted(() => {
console.log('组件已销毁');
});
}
};
Vue3 把生命周期钩子整合到了 Composition API 中,用函数形式调用,逻辑更集中!但如果不熟悉新的钩子用法,很容易写错地方,导致代码出问题。
悬念答案大揭秘
- Vue2 大量动态添加属性:可以封装一个函数,遍历对象并使用Vue.set批量处理;或者直接替换整个对象,触发响应式更新。
- Vue2 深层组件传值:使用 Vuex 进行状态管理,或者借助事件总线(Event Bus),在组件间通过发布 - 订阅模式传递数据。
- Vue2 计算属性依赖异步数据:可以先定义一个中间状态,在异步数据返回后更新中间状态,计算属性依赖中间状态,从而保证结果正确。
- Vue2 多个异步请求按序渲染:使用async/await配合Promise.all,确保所有请求完成后再更新数据,触发视图渲染。
这些技巧哪个最让你心动?
上面 4 组 Vue2 和 Vue3 的技巧,哪个让你觉得 “相见恨晚”?你在开发中还遇到过哪些奇葩问题?快来评论区分享