Skip to content

Vue 3 實戰:從零到一封裝一個 useMouse Composable

在 Vue 3 中,將元件邏輯「去中心化」是保持程式碼整潔的關鍵。今天我們以常見的 滑鼠座標監聽 為例,示範如何從傳統的元件件寫法,重構成靈活的 Composable 函數。

初始狀態:在單一元件 (SFC) 中撰寫

通常我們會直接在 App.vue 寫入監聽邏輯。雖然功能正常,但若其他元件也需要同樣的功能,就必須複製貼上,這違反了 DRY (Don't Repeat Yourself) 原則。

vue
// App.vue
<script setup>
import { ref, onMounted, onUnmounted } from "vue";

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);
});
</script>

<template>
  <p>Mouse Position: {{ x }}, {{ y }}</p>
</template>

進階重構:封裝成可重複使用的邏輯函數

為了達到多元件共享,我們根據 Composable 的設計規範,在 composables 目錄下建立專屬檔案,並以 use 開頭命名。

實作 useMouse.js

核心邏輯保持不變,但我們將狀態與行為封裝在函數中,並 回傳 需要暴露給外部使用的響應式屬性。

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

export default () => {
  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 };
};

最終成果:在元件中優雅呼叫

重構後的元件變得極度簡潔,所有的複雜邏輯都被抽離到 useMouse 之中,元件只負責讀取數據與渲染 UI。

vue
// App.vue
<script setup>
import useMouse from "@/composables/useMouse";

const { x, y } = useMouse();
</script>

<template>
  <p>Mouse Position: {{ x }}, {{ y }}</p>
</template>

核心要點筆記

獨立狀態

每一個引用 useMouse 的元件,都會擁有自己獨立的 xy 狀態。即使在不同元件中同時使用,數據也不會相互干擾或衝突。

清理機制

onUnmounted 封裝在 Composable 內部,確保了當元件銷毀時,監聽器會自動移除,有效防止記憶體洩漏 (Memory Leak)。

高可讀性

透過解構賦值,我們可以一眼看出元件使用了哪些外部邏輯,提升了大型專案的可維護性。