Vue 3 Composition API实战指南

Vue 3 Composition API简介

Vue 3引入了Composition API,这是对Options API的重大改进。Composition API提供了更灵活、更强大的代码组织方式,特别适合大型复杂应用。

基础使用

setup函数

import { ref, reactive, computed } from "vue";

export default {
  setup() {
    // 响应式数据
    const count = ref(0);
    const user = reactive({
      name: "John",
      age: 25
    });
    
    // 计算属性
    const doubleCount = computed(() => count.value * 2);
    
    // 方法
    const increment = () => {
      count.value++;
    };
    
    // 返回模板可用的数据和方法
    return {
      count,
      user,
      doubleCount,
      increment
    };
  }
};

Script Setup语法糖

<script setup>
import { ref, reactive, computed } from "vue";

// 响应式数据
const count = ref(0);
const user = reactive({
  name: "John",
  age: 25
});

// 计算属性
const doubleCount = computed(() => count.value * 2);

// 方法
const increment = () => {
  count.value++;
};
</script>

<template>
  <div>
    <p>Count: {{ count }}</p>
    <p>Double: {{ doubleCount }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

响应式系统

ref vs reactive

import { ref, reactive, isRef, unref } from "vue";

// ref - 用于基本类型和对象引用
const count = ref(0);
const userRef = ref({ name: "John" });

// reactive - 用于对象
const user = reactive({
  name: "John",
  age: 25,
  address: {
    city: "New York"
  }
});

// 类型检查
console.log(isRef(count)); // true
console.log(isRef(user)); // false

// 解包ref
const rawValue = unref(count); // 0

响应式工具函数

import {
  toRef,
  toRefs,
  isProxy,
  isReactive,
  isReadonly,
  markRaw
} from "vue";

const user = reactive({
  name: "John",
  age: 25
});

// 将reactive属性转换为ref
const nameRef = toRef(user, "name");

// 解构reactive对象并保持响应性
const { name, age } = toRefs(user);

// 检查类型
console.log(isProxy(user)); // true
console.log(isReactive(user)); // true
console.log(isReadonly(user)); // false

// 标记为非响应式
const staticConfig = markRaw({
  apiUrl: "https://api.example.com",
  timeout: 5000
});

组合式函数

创建可复用的逻辑

// useCounter.js
export function useCounter(initialValue = 0) {
  const count = ref(initialValue);
  
  const increment = () => {
    count.value++;
  };
  
  const decrement = () => {
    count.value--;
  };
  
  const reset = () => {
    count.value = initialValue;
  };
  
  return {
    count,
    increment,
    decrement,
    reset
  };
}

// 在组件中使用
import { useCounter } from "./useCounter";

const { count, increment } = useCounter(10);

鼠标位置跟踪示例

// useMouse.js
import { ref, onMounted, onUnmounted } from "vue";

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

// 在组件中使用
const { x, y } = useMouse();

生命周期钩子

import {
  onBeforeMount,
  onMounted,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  onUnmounted,
  onErrorCaptured
} from "vue";

export default {
  setup() {
    onBeforeMount(() => {
      console.log("组件即将挂载");
    });
    
    onMounted(() => {
      console.log("组件已挂载");
      // 执行DOM操作或API调用
    });
    
    onBeforeUpdate(() => {
      console.log("组件即将更新");
    });
    
    onUpdated(() => {
      console.log("组件已更新");
    });
    
    onBeforeUnmount(() => {
      console.log("组件即将卸载");
      // 清理工作
    });
    
    onUnmounted(() => {
      console.log("组件已卸载");
    });
    
    onErrorCaptured((error, instance, info) => {
      console.error("捕获到错误:", error);
      // 错误处理逻辑
      return false; // 阻止错误继续向上传播
    });
  }
};

依赖注入

// 父组件提供数据
import { provide, ref } from "vue";

export default {
  setup() {
    const theme = ref("light");
    const user = ref({ name: "John" });
    
    // 提供数据给子组件
    provide("theme", theme);
    provide("user", user);
    
    // 提供方法
    provide("updateTheme", (newTheme) => {
      theme.value = newTheme;
    });
  }
};

// 子组件注入数据
import { inject } from "vue";

export default {
  setup() {
    // 注入数据
    const theme = inject("theme");
    const user = inject("user");
    const updateTheme = inject("updateTheme");
    
    // 提供默认值
    const config = inject("config", { timeout: 3000 });
    
    // 工厂函数
    const api = inject("api", () => {
      return {
        fetchData: () => { /* ... */ }
      };
    });
    
    return {
      theme,
      user,
      updateTheme,
      config,
      api
    };
  }
};

TypeScript支持

import { defineComponent, ref, Ref } from "vue";

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

interface CounterReturn {
  count: Ref<number>;
  increment: () => void;
  reset: () => void;
}

// 类型安全的组合式函数
function useCounter(initialValue: number = 0): CounterReturn {
  const count = ref(initialValue);
  
  const increment = () => {
    count.value++;
  };
  
  const reset = () => {
    count.value = initialValue;
  };
  
  return {
    count,
    increment,
    reset
  };
}

// 类型安全的组件
export default defineComponent({
  name: "UserProfile",
  props: {
    userId: {
      type: Number,
      required: true
    },
    showEmail: {
      type: Boolean,
      default: false
    }
  },
  setup(props) {
    const user = ref<User | null>(null);
    const loading = ref(false);
    
    // 类型推断正常工作
    const { count, increment } = useCounter();
    
    const fetchUser = async () => {
      loading.value = true;
      try {
        const response = await fetch(`/api/users/${props.userId}`);
        user.value = await response.json();
      } finally {
        loading.value = false;
      }
    };
    
    onMounted(() => {
      fetchUser();
    });
    
    return {
      user,
      loading,
      count,
      increment,
      fetchUser
    };
  }
});

性能优化

响应式数据优化

import { shallowRef, shallowReactive, readonly } from "vue";

// shallowRef - 只对.value变化响应
const nestedObject = shallowRef({
  deep: { value: 1 }
});
// 以下变化不会触发更新
nestedObject.value.deep.value = 2;
// 需要替换整个对象
nestedObject.value = { deep: { value: 2 } };

// shallowReactive - 只对根级属性响应
const shallowUser = shallowReactive({
  name: "John",
  address: { city: "NYC" }
});
// 以下变化会触发更新
shallowUser.name = "Jane";
// 以下变化不会触发更新
shallowUser.address.city = "LA";

// readonly - 创建只读代理
const protectedConfig = readonly({
  apiUrl: "https://api.example.com"
});
// 尝试修改会触发警告
protectedConfig.apiUrl = "https://new.example.com"; // 警告!

计算属性缓存

import { computed, ref } from "vue";

const items = ref([
  { id: 1, name: "Item 1", price: 10 },
  { id: 2, name: "Item 2", price: 20 },
  { id: 3, name: "Item 3", price: 30 }
]);

// 缓存的计算属性
const totalPrice = computed(() => {
  console.log("计算总价...");
  return items.value.reduce((sum, item) => sum + item.price, 0);
});

// 多次访问只计算一次
console.log(totalPrice.value); // 计算总价... 60
console.log(totalPrice.value); // 60 (从缓存读取)

// 当依赖项变化时重新计算
items.value.push({ id: 4, name: "Item 4", price: 40 });
console.log(totalPrice.value); // 计算总价... 100

最佳实践

  1. 按功能组织代码:将相关逻辑放在同一个组合式函数中
  2. 合理使用ref和reactive:基本类型用ref,对象用reactive
  3. 利用TypeScript:为组合式函数和组件提供完整的类型定义
  4. 性能优化:在适当场景使用shallowRef、shallowReactive
  5. 错误处理:使用onErrorCaptured捕获和处理错误
  6. 代码复用:将通用逻辑提取为组合式函数

总结

Vue 3的Composition API为Vue应用开发带来了革命性的改进。通过更灵活的代码组织方式、更好的TypeScript支持和更强大的逻辑复用能力,Composition API使得构建大型复杂应用变得更加容易和高效。

关键要点:

  • 使用setup函数或<script setup>语法糖
  • 掌握ref和reactive的区别与使用场景
  • 利用组合式函数实现逻辑复用
  • 合理使用生命周期钩子和依赖注入
  • 充分利用TypeScript的类型安全特性

通过实践这些概念和技巧,你将能够充分利用Composition API的优势,构建出更健壮、更可维护的Vue 3应用。

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

请登录后发表评论

    暂无评论内容