Vue 3.5 Composition API最佳实践:从入门到精通

Vue 3.5 Composition API最佳实践:从入门到精通

Vue 3.5带来了更强大的Composition API,彻底改变了我们组织Vue组件逻辑的方式。本文将深入探讨Composition API的最佳实践,帮助你写出更清晰、更可维护的Vue代码。

1. 为什么选择Composition API?

在Options API中,相关逻辑被分散到不同的选项中(data、methods、computed等)。Composition API允许我们按功能组织代码,让相关逻辑保持在一起。

// Options API - 逻辑分散
{
  data() {
    return {
      count: 0,
      searchQuery: "",
      filteredItems: []
    };
  },
  methods: {
    increment() {
      this.count++;
    },
    filterItems() {
      // 过滤逻辑
    }
  },
  computed: {
    doubleCount() {
      return this.count * 2;
    }
  },
  watch: {
    searchQuery() {
      this.filterItems();
    }
  }
}

// Composition API - 按功能组织
import { ref, computed, watch } from "vue";

// 计数器功能
function useCounter() {
  const count = ref(0);
  const doubleCount = computed(() => count.value * 2);
  
  function increment() {
    count.value++;
  }
  
  return { count, doubleCount, increment };
}

// 搜索功能
function useSearch(items) {
  const searchQuery = ref("");
  const filteredItems = computed(() => {
    return items.filter(item => 
      item.name.includes(searchQuery.value)
    );
  });
  
  return { searchQuery, filteredItems };
}

2. 自定义Composable:代码复用的艺术

Composable是Composition API的核心概念,让我们可以创建可复用的逻辑单元。

// useMouse.js - 鼠标位置跟踪
import { ref, onMounted, onUnmounted } from "vue";

export function useMouse() {
  const x = ref(0);
  const y = ref(0);
  
  function update(event) {
    x.value = event.pageX;
    y.value = event.pageY;
  }
  
  onMounted(() => window.addEventListener("mousemove", update));
  onUnmounted(() => window.removeEventListener("mousemove", update));
  
  return { x, y };
}

// useFetch.js - 数据获取
import { ref } from "vue";

export function useFetch(url) {
  const data = ref(null);
  const error = ref(null);
  const loading = ref(false);
  
  async function fetchData() {
    loading.value = true;
    try {
      const response = await fetch(url);
      data.value = await response.json();
    } catch (err) {
      error.value = err;
    } finally {
      loading.value = false;
    }
  }
  
  // 自动执行
  fetchData();
  
  return { data, error, loading, refetch: fetchData };
}

// useLocalStorage.js - 本地存储
import { ref, watch } from "vue";

export function useLocalStorage(key, defaultValue) {
  const data = ref(JSON.parse(
    localStorage.getItem(key) || JSON.stringify(defaultValue)
  ));
  
  watch(data, (newValue) => {
    localStorage.setItem(key, JSON.stringify(newValue));
  }, { deep: true });
  
  return data;
}

3. 响应式进阶技巧

Vue 3.5提供了更强大的响应式工具。

import { ref, reactive, computed, watch, watchEffect, toRefs } from "vue";

// 1. ref vs reactive
// ref: 用于基本类型,通过.value访问
const count = ref(0);

// reactive: 用于对象,自动深度响应式
const user = reactive({
  name: "张三",
  age: 30,
  address: {
    city: "北京",
    street: "朝阳路"
  }
});

// 2. computed的妙用
const fullName = computed(() => `${user.name} (${user.age}岁)`);

// 带setter的computed
const editableFullName = computed({
  get() {
    return `${user.name} ${user.age}`;
  },
  set(newValue) {
    const [name, age] = newValue.split(" ");
    user.name = name;
    user.age = parseInt(age);
  }
});

// 3. watch的高级用法
// 深度监听对象
watch(
  () => user.address,
  (newAddress, oldAddress) => {
    console.log("地址变化:", newAddress);
  },
  { deep: true }
);

// 监听多个源
watch(
  [() => user.name, () => user.age],
  ([newName, newAge], [oldName, oldAge]) => {
    console.log(`姓名从${oldName}变为${newName}`);
    console.log(`年龄从${oldAge}变为${newAge}`);
  }
);

// 4. watchEffect - 自动依赖收集
watchEffect(() => {
  console.log(`用户信息更新: ${user.name}, ${user.age}`);
  // 自动追踪user.name和user.age的依赖
});

// 5. toRefs - 解构响应式对象
const { name, age } = toRefs(user);
// 现在name和age仍然是响应式的ref

4. 组件通信新模式

Composition API提供了新的组件通信方式。

// 父组件
import { provide } from "vue";
import ChildComponent from "./ChildComponent.vue";

export default {
  components: { ChildComponent },
  setup() {
    const theme = reactive({
      primary: "#1890ff",
      secondary: "#52c41a"
    });
    
    // 提供数据给所有子组件
    provide("theme", theme);
    
    return { theme };
  }
};

// 子组件
import { inject } from "vue";

export default {
  setup() {
    // 注入父组件提供的数据
    const theme = inject("theme");
    
    return { theme };
  }
};

// 带默认值的注入
const theme = inject("theme", {
  primary: "#000000",
  secondary: "#ffffff"
});

// 工厂函数注入
const user = inject("user", () => {
  // 仅在需要时创建默认值
  return { name: "Guest", role: "viewer" };
});

5. TypeScript集成最佳实践

Vue 3.5与TypeScript的集成更加完美。

// 类型安全的Composable
import { ref, Ref } from "vue";

interface User {
  id: number;
  name: string;
  email: string;
}

// 明确指定返回类型
export function useUser(): {
  user: Ref;
  loading: Ref;
  fetchUser: (id: number) => Promise;
} {
  const user = ref(null);
  const loading = ref(false);
  
  async function fetchUser(id: number) {
    loading.value = true;
    try {
      const response = await fetch(`/api/users/${id}`);
      user.value = await response.json();
    } finally {
      loading.value = false;
    }
  }
  
  return { user, loading, fetchUser };
}

// 组件Props类型定义
import { defineComponent, PropType } from "vue";

interface Product {
  id: number;
  name: string;
  price: number;
  category: string;
}

export default defineComponent({
  props: {
    // 基本类型
    title: {
      type: String,
      required: true
    },
    // 复杂类型
    product: {
      type: Object as PropType,
      required: true
    },
    // 数组类型
    tags: {
      type: Array as PropType,
      default: () => []
    },
    // 联合类型
    status: {
      type: String as PropType<"active" | "inactive" | "pending">,
      default: "active"
    }
  },
  setup(props) {
    // props有完整的类型提示
    console.log(props.product.name);
    
    return {};
  }
});

6. 性能优化技巧

// 1. 使用shallowRef/shallowReactive减少不必要的深度响应式
import { shallowRef, shallowReactive } from "vue";

// 大型对象,内部变化不需要响应式
const largeConfig = shallowReactive({
  // 数千个配置项
});

// 2. 使用markRaw跳过响应式转换
import { markRaw } from "vue";

const heavyLibrary = markRaw({
  // 第三方库实例,不需要响应式
  heavyMethod() { /* ... */ }
});

// 3. 合理使用computed缓存
const expensiveCalculation = computed(() => {
  // 复杂计算,结果会被缓存
  return performHeavyCalculation(data.value);
});

// 4. 使用watch的flush选项控制触发时机
watch(
  source,
  () => {
    // DOM更新后执行
  },
  { flush: "post" }
);

// 5. 使用effectScope管理副作用
import { effectScope } from "vue";

const scope = effectScope();

scope.run(() => {
  // 在这个作用域内的所有响应式效果
  const doubled = computed(() => count.value * 2);
  watch(count, () => console.log("count changed"));
});

// 一次性清理所有副作用
scope.stop();

总结

Vue 3.5的Composition API为我们提供了更灵活、更强大的工具来组织Vue应用逻辑。通过自定义Composable、合理的响应式使用、类型安全的TypeScript集成以及性能优化技巧,我们可以构建出更健壮、更易维护的Vue应用。

关键要点:

  1. 按功能组织代码,而不是按选项
  2. 创建可复用的Composable函数
  3. 理解ref和reactive的适用场景
  4. 充分利用TypeScript的类型系统
  5. 注意性能优化,避免不必要的响应式

随着Vue生态的不断发展,Composition API将成为Vue开发的标准模式。掌握这些最佳实践,你将在Vue开发中游刃有余。

© 版权声明
THE END
喜欢就支持一下吧
点赞14 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容