Vue3确实好用,但用不好也容易给自己挖坑。下面我结合平时写代码的经验,分享几个非常实用的技术点
刚转Vue3的时候,很多人都在纠结ref和reactive。其实不用想太复杂,记住一个口诀就行:基础类型无脑用ref,复杂对象优先用reactive。
用reactive写对象确实清爽,不用到处写.value,但有个致命坑:绝对不能直接重新赋值整个对象,不然响应式就丢了!
<script setup>
import { reactive } from 'vue';
const user = reactive({ name: '张三', age: 20 });
function changeUser() {
// 错误写法:直接赋值,页面不会更新,响应式丢失!
// user = { name: '李四', age: 22 };
// 正确写法1:直接修改属性
user.name = '李四';
// 正确写法2:如果非要整体替换,用Object.assign合并
Object.assign(user, { name: '李四', age: 22 });
}
</script>
如果你嫌麻烦,或者项目里既有基础类型又有对象,那就统一全用ref,虽然多写几个.value,但绝对安全,不容易丢响应式。
监听数据变化也是高频操作,这俩API千万别混用。
<script setup>
import { ref, watch, watchEffect } from 'vue';
const count = ref(0);
const keyword = ref('');
// watch:明确指定监听谁,能拿到新旧值,且是“惰性”的(初始不执行)
watch(count, (newVal, oldVal) => {
console.log(`count从 ${oldVal} 变成了 ${newVal}`);
});
// watchEffect:自动收集依赖,一上来就立即执行一次,且拿不到旧值
watchEffect(() => {
console.log(`当前keyword是:${keyword.value}`);
});
</script>
实战建议:如果你需要对比新旧值,或者只想在数据变化时触发,用watch;如果你只是想在初始化时执行一次,且后续依赖的变量变了自动重新执行,用watchEffect更方便。
以前Vue2用Mixin复用逻辑,经常遇到命名冲突、不知道变量从哪来的问题。Vue3的自定义Hook完美解决了这个痛点。比如我们封装一个通用的“求和”逻辑:
// hooks/useSum.ts
import { ref, onMounted } from 'vue';
export default function () {
let sum = ref(0);
const increment = () => { sum.value += 1; };
const decrement = () => { sum.value -= 1; };
onMounted(() => {
console.log('组件挂载完毕,初始求和为:', sum.value);
});
// 把需要的数据和方法暴露出去
return { sum, increment, decrement };
}
在组件里用的时候,就像调用普通函数一样,逻辑极其清晰:
<script setup lang="ts">
import useSum from './hooks/useSum';
// 直接解构使用,干净利落
const { sum, increment, decrement } = useSum();
</script>
既然上了Vue3,强烈建议搭配TS使用。接收父组件传值时,用defineProps配合泛型,代码提示和类型检查直接拉满:
<script setup lang="ts">
// 1. 先定义好接口类型
export interface PersonInter {
id: string;
name: string;
age: number;
}
// 2. 接收props,限制类型 + 设置默认值 + 限制必要性
// 这里表示list是可选的,如果不传,就给一个默认数组
const props = withDefaults(defineProps<{ list?: PersonInter[] }>(), {
list: () => [{ id: '01', name: '默认用户', age: 18 }]
});
</script>
这种写法不仅代码量少,而且父组件传错类型时,编辑器立马就会报红警告,能帮你提前干掉80%的低级Bug。
最后提一嘴生命周期。Vue3把beforeCreate和created合并到了setup里,因为setup本身就是在这两个阶段之间执行的。日常开发中最常用的就是下面这几个:
<script setup>
import { onMounted, onUpdated, onBeforeUnmount } from 'vue';
// 挂载完毕:通常在这里发请求、初始化第三方插件
onMounted(() => {
console.log('组件挂载完毕');
});
// 更新完毕:数据变化导致DOM更新后触发
onUpdated(() => {
console.log('DOM更新完毕');
});
// 卸载之前:记得在这里清理定时器、解绑全局事件,防止内存泄漏
onBeforeUnmount(() => {
console.log('准备卸载,清理资源');
});
</script>
vue — 2023 / 03/12