Vue 3.5 Composition API深度解析与最佳实践

从 Options API 到 Composition API

Vue 3 引入 Composition API,本质上是为了解决大型组件中“相关逻辑被迫按 data/methods/computed 分散”的问题。在 Options API 里,一个功能的代码被拆散到不同选项;组件一变大,逻辑就难以追踪。Composition API 让你把同一功能的响应式状态、计算、副作用聚拢到一起,并通过组合式函数(composables)实现跨组件复用。

Vue 3.5 进一步优化了响应式系统,内存占用更低、性能更好,useTemplateRefuseId 等新 API 让开发体验更顺滑。

响应式核心:ref 与 reactive

Vue 3 的响应式基于两个原语:refreactive

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

const count = ref(0);          // 基本类型用 ref
console.log(count.value);      // 访问需 .value
count.value++;

const state = reactive({       // 对象用 reactive
  user: { name: "Tom" },
  list: [],
});
state.user.name = "Jerry";     // 直接访问,无需 .value

它们的区别和取舍:

  • reactive 只能用于对象/数组,返回 Proxy 代理,访问无须 .value,但解构会失去响应性。

  • ref 适用于任何类型(包括基本类型),通过 .value 访问。在模板中会自动解包,无需 .value
  • 一个实用原则:基本类型用 ref,对象也优先用 ref,这样可以保持一致的访问方式,避免 reactive 解构丢响应性的坑。当然,整块表单状态用 reactive 也完全可以。

    javascript
    import { ref, reactive, toRefs } from "vue";
    
    // reactive 解构丢响应性,需 toRefs
    const state = reactive({ count: 0, name: "Vue" });
    const { count, name } = toRefs(state); // 现在 count/name 是 ref

    computed 与 watch

    computed 是派生状态,带缓存;watch 监听变化执行副作用。

    javascript
    import { ref, computed, watch } from "vue";
    
    const price = ref(100);
    const quantity = ref(2);
    
    // 计算属性,自动缓存
    const total = computed(() => price.value * quantity.value);
    
    // 监听多个源
    watch([price, quantity], ([newPrice, newQty]) => {
      console.log(`价格或数量变化: ${newPrice} x ${newQty}`);
    }, { immediate: false });
    
    // watchEffect 自动收集依赖
    watchEffect(() => {
      console.log("当前总价:", total.value);
    });

    watch 显式指定监听源,能拿到新旧值;watchEffect 自动追踪内部用到的响应式数据,立即执行一次,适合“依赖谁就监听谁”的场景。

    组合式函数:逻辑复用的标准范式

    组合式函数是 Composition API 的精华,命名约定以 use 开头。它替代了 Vue 2 时代的 mixin,避免了命名冲突和来源不清晰的问题。

    javascript
    import { ref, onMounted, onUnmounted } from "vue";
    
    // 鼠标位置追踪
    export function useMouse() {
      const x = ref(0);
      const y = ref(0);
    
      function update(e) {
        x.value = e.pageX;
        y.value = e.pageY;
      }
    
      onMounted(() => window.addEventListener("mousemove", update));
      onUnmounted(() => window.removeEventListener("mousemove", update));
    
      return { x, y };
    }

    在组件中使用:

    vue
    <script setup>
    import { useMouse } from "./composables/useMouse";
    
    const { x, y } = useMouse();
    </script>
    
    <template>
      <p>鼠标位置: {{ x }}, {{ y }}</p>
    </template>

    组合式函数的优势:来源清晰、可类型推导、易于测试、可在多个组件复用。一个良好组织的项目,应当把可复用逻辑都抽成 composables。

    <script setup> 语法糖

    <script setup> 是 Vue 3 推荐的编写组件方式,编译后更高效,且天然支持顶层 await。

    vue
    <script setup>
    import { ref, computed } from "vue";
    import { useFetch } from "./composables/useFetch";
    
    const { data, error } = await useFetch("/api/user");
    const title = ref("用户列表");
    const count = computed(() => data.value?.length ?? 0);
    
    // 定义 props 与 emits
    const props = defineProps({ id: Number });
    const emit = defineEmits(["refresh"]);
    </script>

    状态管理:Pinia

    Pinia 是 Vue 官方推荐的状态管理库,比 Vuex 更简洁、对 TS 更友好。它去掉了 mutation,直接在 actions 里修改状态。

    javascript
    import { defineStore } from "pinia";
    
    export const useUserStore = defineStore("user", {
      state: () => ({
        users: [],
        loading: false,
      }),
      getters: {
        count: (state) => state.users.length,
      },
      actions: {
        async fetchUsers() {
          this.loading = true;
          try {
            const res = await fetch("/api/users");
            this.users = await res.json();
          } finally {
            this.loading = false;
          }
        },
      },
    });

    在组合式 API 里也可以用 setup 风格的 store,配合 storeToRefs 解构保持响应性:

    javascript
    import { storeToRefs } from "pinia";
    import { useUserStore } from "./stores/user";
    
    const store = useUserStore();
    const { users, count } = storeToRefs(store); // 保持响应性
    store.fetchUsers();                           // action 可直接解构调用

    性能优化技巧


  • shallowRef / shallowReactive:只对顶层响应,避免深层数据的递归代理开销,适合大列表。

  • v-memo:缓存子树,只有指定依赖变化才重新渲染。

  • 异步组件 + 路由懒加载:减小首屏体积。

  • markRaw:标记某些对象永不响应(如第三方实例),避免无谓代理。

  • 合理使用 computed:替代在模板里写复杂表达式,享受缓存红利。
  • vue
    <template>
      <!-- v-memo: 仅当 item.id 变化时才重渲染这一项 -->
      <div v-for="item in list" :key="item.id" v-memo="[item.id]">
        {{ item.name }}
      </div>
    </template>

    常见陷阱


  • 解构 reactive 丢响应性:用 toRefs

  • watch 里监听 reactive 对象属性:要用函数返回值 () => state.count,或直接监听 ref

  • ref 在 reactive 内会自动解包reactive({ count: ref(0) }) 访问 state.count 直接是值,无需 .value

  • 避免在模板里写过多逻辑:抽成 computed,可读性和性能都更好。
  • 小结

    Composition API 的价值在于“按功能组织代码 + 函数级复用”。掌握 ref/reactivecomputed/watch 和组合式函数,你就拿到了 Vue 3 的核心生产力。配合 <script setup> 的简洁语法和 Pinia 的状态管理,无论是中小型应用还是大型后台,都能写出结构清晰、易于维护的代码。Vue 3.5 的响应式优化和新增 API,则让这套体系在性能和体验上更上一层楼。

    💬 评论区 (0)

    暂无评论,快来抢沙发吧!