Vue3开发避坑与实战指南干货分享

前言

Vue3确实好用,但用不好也容易给自己挖坑。下面我结合平时写代码的经验,分享几个非常实用的技术点

一、ref和reactive到底怎么选?

刚转Vue3的时候,很多人都在纠结refreactive。其实不用想太复杂,记住一个口诀就行:基础类型无脑用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,但绝对安全,不容易丢响应式。

二、watch和watchEffect的区别

监听数据变化也是高频操作,这俩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更方便。

三、自定义Hook(Composables)真香

以前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>

四、TypeScript下的Props怎么写最规范

既然上了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把beforeCreatecreated合并到了setup里,因为setup本身就是在这两个阶段之间执行的。日常开发中最常用的就是下面这几个:

<script setup>
import { onMounted, onUpdated, onBeforeUnmount } from 'vue';

// 挂载完毕:通常在这里发请求、初始化第三方插件
onMounted(() => {
  console.log('组件挂载完毕');
});

// 更新完毕:数据变化导致DOM更新后触发
onUpdated(() => {
  console.log('DOM更新完毕');
});

// 卸载之前:记得在这里清理定时器、解绑全局事件,防止内存泄漏
onBeforeUnmount(() => {
  console.log('准备卸载,清理资源');
});
</script>

— 2023 / 03/12