VueUse 完整教學 — Vue 3 Composition Utilities 實戰指南

1. 專案定位

VueUse 是 Vue 3 生態系中最受歡迎的 Composition API 工具函式庫,由 Anthony Fu(Vue / Vite / Nuxt 核心團隊成員)主導開發。專案自 2019 年底啟動,截至 2026 年 6 月已累積超過 22,000 個 GitHub Stars、2,900 個 Forks,npm 月下載量超過百萬。

為什麼需要 VueUse?

Vue 3 的 Composition API 提供了強大的邏輯組合能力,但在實際開發中,許多常見的瀏覽器 API 操作(如滑鼠追蹤、本地儲存、網路狀態偵測)都需要重複撰寫樣板程式碼。VueUse 將這些常見需求封裝為即用型 composable 函式,讓開發者可以一行引入、立即使用。

核心價值

  • 200+ 個 composable 函式:涵蓋瀏覽器 API、感測器、狀態管理、動畫、網路、元素操作等領域
  • 完整 TypeScript 支援:所有函式皆有完善的型別定義
  • Tree-shakable:只打包實際使用的函式,不增加多餘體積
  • SSR 相容:所有函式都能在 Server-Side Rendering 環境安全運行
  • Nuxt 3 整合:內建 @vueuse/nuxt 模組,支援 auto-import
  • 零依賴核心@vueuse/core 僅依賴 vue@vueuse/shared

與類似專案的比較

特性VueUseReact Hooks (ahooks)Solid Primitives
框架Vue 3ReactSolid.js
函式數量200+60+40+
TypeScript原生原生原生
SSR 支援完善部分完善
Tree-shaking完善完善完善
社群活躍度極高

2. 安裝指南

基本安裝

1# 使用 npm
2npm i @vueuse/core
3
4# 使用 pnpm(推薦)
5pnpm add @vueuse/core
6
7# 使用 yarn
8yarn add @vueuse/core

系統需求

  • Vue 3.5+(Composition API 必須可用)
  • Node.js 18+(開發環境)
  • TypeScript 5.0+(選用但強烈建議)

附加套件安裝

 1# 第三方整合(Axios, Fuse.js, QRCode 等)
 2pnpm add @vueuse/integrations
 3
 4# 數學運算
 5pnpm add @vueuse/math
 6
 7# Vue Router 整合
 8pnpm add @vueuse/router
 9
10# RxJS 整合
11pnpm add @vueuse/rxjs
12
13# Firebase 整合
14pnpm add @vueuse/firebase
15
16# Electron 整合
17pnpm add @vueuse/electron

Nuxt 3 整合

1pnpm add @vueuse/nuxt
1// nuxt.config.ts
2export default defineNuxtConfig({
3  modules: ['@vueuse/nuxt'],
4})

安裝後所有 VueUse 函式皆可直接在元件中使用,無需手動 import。

CDN 使用

1<script src="https://unpkg.com/@vueuse/shared"></script>
2<script src="https://unpkg.com/@vueuse/core"></script>

全域變數 VueUse 將可用。

3. 核心架構解析

Monorepo 結構

VueUse 採用 pnpm workspace + Turborepo 管理的 monorepo 架構,將功能劃分為多個獨立的 npm 套件:


graph TB
    subgraph "VueUse Monorepo"
        SHARED["@vueuse/shared
共享工具 ~30 個
reactivity helpers / filters / state factories"] CORE["@vueuse/core
核心 composables ~155 個
Browser / Sensors / State / Elements"] MATH["@vueuse/math
數學運算 ~18 個"] INTEG["@vueuse/integrations
第三方整合 ~12 個
Axios / Fuse / JWT / Cookies"] ROUTER["@vueuse/router
Vue Router 整合 3 個"] RXJS["@vueuse/rxjs
RxJS 整合 ~7 個"] FIREBASE["@vueuse/firebase
Firebase 整合 3 個"] ELECTRON["@vueuse/electron
Electron 整合 5 個"] NUXT["@vueuse/nuxt
Nuxt 模組 auto-import"] META["@vueuse/metadata
函式 metadata"] end SHARED --> CORE SHARED --> MATH SHARED --> INTEG SHARED --> ROUTER SHARED --> RXJS CORE --> NUXT CORE --> META style SHARED fill:#42b883,color:#fff style CORE fill:#35495e,color:#fff

Composable 設計模式

每個 composable 遵循一致的設計模式:

  1. 輸入:接受 MaybeRefOrGetter<T> 型別的參數,同時支援靜態值、ref 和 getter
  2. 輸出:回傳包含 reactive refs 的物件(解構友善)
  3. 生命週期:自動在 component unmount 時清理副作用(event listeners、timers 等)
  4. SSR 安全:透過 defaultWindow / defaultDocument 抽象,避免在 SSR 環境存取瀏覽器 API
  5. 可配置性:透過 options 物件提供細緻的控制(如 throttleeventFilterwindow
 1// 典型的 composable 結構
 2export function useXxx(
 3  target: MaybeRefOrGetter<HTMLElement>,  // 響應式輸入
 4  options?: UseXxxOptions                  // 可選配置
 5): UseXxxReturn {                          // 型別化的回傳
 6  // 1. 將輸入轉為 ref
 7  const targetRef = toRef(target)
 8
 9  // 2. 建立響應式狀態
10  const state = shallowRef(initialValue)
11
12  // 3. 設定副作用(自動清理)
13  useEventListener(targetRef, 'event', handler)
14
15  // 4. 回傳響應式資料
16  return { state, /* ... */ }
17}

核心分類

@vueuse/core 的 155+ 個 composables 可歸類為以下幾大領域:

分類代表函式說明
BrowseruseClipboard, useFetch, useStorage, useTitle瀏覽器 API 封裝
SensorsuseMouse, useGeolocation, useDeviceMotion感測器與輸入裝置
StateuseLocalStorage, useSessionStorage, useRefHistory狀態管理與持久化
ElementsuseElementSize, useIntersectionObserver, useDraggableDOM 元素操作
ComponentuseVModel, createReusableTemplate, templateRef元件工具
WatchwatchDebounced, watchThrottled, watchPausable進階 watch
NetworkuseWebSocket, useEventSource, useOnline網路通訊
AnimationuseTransition, useRafFn, useAnimate動畫與計時
UtilitiesuseDark, useColorMode, useBreakpoints通用工具

4. 常用 Composables 詳細用法

4.1 useMouse — 滑鼠位置追蹤

追蹤滑鼠在頁面上的即時位置。

 1<script setup lang="ts">
 2import { useMouse } from '@vueuse/core'
 3
 4const { x, y, sourceType } = useMouse()
 5// x: ShallowRef<number> — 滑鼠 X 座標
 6// y: ShallowRef<number> — 滑鼠 Y 座標
 7// sourceType: ShallowRef<'mouse' | 'touch' | null>
 8</script>
 9
10<template>
11  <div>滑鼠位置{{ x }}, {{ y }}來源{{ sourceType }}</div>
12</template>

進階選項:

1const { x, y } = useMouse({
2  type: 'page',        // 'page' | 'client' | 'screen' | 'movement'
3  touch: true,         // 是否追蹤觸控事件
4  resetOnTouchEnds: false,
5  initialValue: { x: 0, y: 0 },
6})

4.2 useLocalStorage / useSessionStorage — 響應式本地儲存

 1<script setup lang="ts">
 2import { useLocalStorage } from '@vueuse/core'
 3
 4// 自動序列化/反序列化,型別推斷
 5const store = useLocalStorage('app-settings', {
 6  theme: 'light',
 7  fontSize: 14,
 8  language: 'zh-TW',
 9})
10
11// 修改 store 會自動同步到 localStorage
12store.value.theme = 'dark'
13
14// 跨分頁同步(storage event)
15// 在另一個分頁修改 localStorage 時,這裡的 ref 也會自動更新
16</script>

自訂序列化器:

1import { useStorage } from '@vueuse/core'
2
3const state = useStorage('key', new Map<string, number>(), localStorage, {
4  serializer: {
5    read: (v: string) => new Map(JSON.parse(v)),
6    write: (v: Map<string, number>) => JSON.stringify([...v]),
7  },
8})

4.3 useFetch — 響應式 HTTP 請求

 1<script setup lang="ts">
 2import { useFetch } from '@vueuse/core'
 3
 4const { data, error, isFetching, isFinished, statusCode, execute } = useFetch(
 5  'https://api.example.com/users',
 6  { immediate: true }  // 預設立即發送
 7).json()               // 自動 parse JSON
 8
 9// 條件式發送
10const url = ref('')
11const { data: user } = useFetch(url, { refetch: true }).json()
12// 當 url 改變時自動重新發送
13
14// 手動重新發送
15await execute()
16</script>

建立可重用的 fetch 實例:

 1import { createFetch } from '@vueuse/core'
 2
 3const useMyFetch = createFetch({
 4  baseUrl: 'https://api.example.com',
 5  options: {
 6    beforeFetch({ options }) {
 7      options.headers = {
 8        ...options.headers,
 9        Authorization: `Bearer ${token.value}`,
10      }
11      return { options }
12    },
13    afterFetch(ctx) {
14      // 全域後處理
15      return ctx
16    },
17  },
18  fetchOptions: {
19    mode: 'cors',
20  },
21})
22
23// 使用
24const { data } = useMyFetch('/users').json()

4.4 useWebSocket — WebSocket 連線管理

 1import { useWebSocket } from '@vueuse/core'
 2
 3const {
 4  data,      // 最新接收的訊息
 5  status,    // 連線狀態:'OPEN' | 'CONNECTING' | 'CLOSED'
 6  send,      // 發送訊息
 7  open,      // 開啟連線
 8  close,     // 關閉連線
 9} = useWebSocket('wss://example.com/ws', {
10  autoReconnect: {
11    retries: 3,
12    delay: 1000,
13    onFailed() {
14      console.error('WebSocket 重連失敗')
15    },
16  },
17  heartbeat: {
18    message: 'ping',
19    interval: 30000,
20    pongTimeout: 10000,
21  },
22  onConnected(ws) {
23    console.log('已連線')
24  },
25  onMessage(ws, event) {
26    console.log('收到訊息:', event.data)
27  },
28})

4.5 useDark / useColorMode — 深色模式

 1import { useDark, useToggle } from '@vueuse/core'
 2
 3const isDark = useDark()
 4const toggleDark = useToggle(isDark)
 5
 6// 或使用 useColorMode 支援更多模式
 7import { useColorMode } from '@vueuse/core'
 8
 9const mode = useColorMode({
10  modes: {
11    light: 'light',
12    dark: 'dark',
13    sepia: 'sepia',
14    'high-contrast': 'high-contrast',
15  },
16  attribute: 'data-theme',  // 設定在哪個 HTML attribute
17  storageKey: 'color-mode', // localStorage key
18})

4.6 useIntersectionObserver — 元素可見性偵測

 1import { useIntersectionObserver } from '@vueuse/core'
 2
 3const target = ref<HTMLElement | null>(null)
 4const isVisible = ref(false)
 5
 6const { stop } = useIntersectionObserver(
 7  target,
 8  ([{ isIntersecting }]) => {
 9    isVisible.value = isIntersecting
10  },
11  {
12    threshold: 0.5,        // 50% 可見時觸發
13    rootMargin: '0px',
14  },
15)

懶載入圖片範例:

 1<script setup lang="ts">
 2import { useIntersectionObserver } from '@vueuse/core'
 3
 4const imageRef = ref<HTMLImageElement | null>(null)
 5const src = ref('')
 6
 7useIntersectionObserver(imageRef, ([{ isIntersecting }]) => {
 8  if (isIntersecting) {
 9    src.value = 'https://example.com/large-image.jpg'
10  }
11})
12</script>
13
14<template>
15  <img ref="imageRef" :src="src" alt="Lazy loaded" />
16</template>

4.7 useDraggable — 拖曳操作

 1import { useDraggable } from '@vueuse/core'
 2
 3const el = ref<HTMLElement | null>(null)
 4
 5const { x, y, style, isDragging } = useDraggable(el, {
 6  initialValue: { x: 100, y: 100 },
 7  onStart(position, event) {
 8    console.log('拖曳開始', position)
 9  },
10  onMove(position, event) {
11    console.log('拖曳中', position)
12  },
13  onEnd(position, event) {
14    console.log('拖曳結束', position)
15  },
16})

4.8 useEventListener — 事件監聽管理

 1import { useEventListener } from '@vueuse/core'
 2
 3// 自動在 component unmount 時移除
 4useEventListener(window, 'resize', (evt) => {
 5  console.log('視窗大小改變')
 6})
 7
 8// 支援多事件
 9useEventListener(document, ['click', 'touchstart'], (evt) => {
10  console.log('互動事件')
11})
12
13// 手動停止
14const cleanup = useEventListener(target, 'scroll', handler)
15cleanup() // 移除監聽

4.9 useBreakpoints — 響應式斷點

 1import { useBreakpoints, breakpointsTailwind } from '@vueuse/core'
 2
 3const breakpoints = useBreakpoints(breakpointsTailwind)
 4
 5const sm = breakpoints.smaller('sm')     // < 640px
 6const md = breakpoints.between('sm', 'md') // 640-768px
 7const lg = breakpoints.greater('lg')     // > 1024px
 8const current = breakpoints.current()    // 目前啟用的斷點名稱陣列
 9
10// 自訂斷點
11const bp = useBreakpoints({
12  mobile: 0,
13  tablet: 640,
14  laptop: 1024,
15  desktop: 1280,
16})

4.10 共享工具(@vueuse/shared)

 1import {
 2  createGlobalState,
 3  createSharedComposable,
 4  createEventHook,
 5  refDebounced,
 6  refThrottled,
 7  watchDebounced,
 8  watchThrottled,
 9  watchPausable,
10} from '@vueuse/shared'
11
12// 全域狀態(跨元件共享)
13const useGlobalStore = createGlobalState(() => {
14  const count = ref(0)
15  const doubled = computed(() => count.value * 2)
16  return { count, doubled }
17})
18
19// 防抖 ref
20const input = ref('')
21const debouncedInput = refDebounced(input, 500) // 500ms 後才更新
22
23// 節流 ref
24const scrollY = ref(0)
25const throttledY = refThrottled(scrollY, 100) // 每 100ms 最多更新一次
26
27// 自訂事件 hook
28const onFileUploaded = createEventHook<{ url: string }>()
29onFileUploaded.on(({ url }) => console.log('上傳完成:', url))
30onFileUploaded.trigger({ url: 'https://...' })

5. 應用場景

場景 1:響應式暗色模式切換

 1<script setup lang="ts">
 2import { useDark, useToggle, usePreferredDark } from '@vueuse/core'
 3
 4const isDark = useDark({
 5  selector: 'html',
 6  attribute: 'class',
 7  valueDark: 'dark',
 8  valueLight: '',
 9})
10const toggleDark = useToggle(isDark)
11const systemPrefersDark = usePreferredDark()
12</script>
13
14<template>
15  <button @click="toggleDark()">
16    {{ isDark ? '切換到亮色' : '切換到暗色' }}
17  </button>
18  <p>系統偏好{{ systemPrefersDark ? '暗色' : '亮色' }}</p>
19</template>

場景 2:無限滾動列表

 1<script setup lang="ts">
 2import { useInfiniteScroll } from '@vueuse/core'
 3
 4const listRef = ref<HTMLElement | null>(null)
 5const items = ref<string[]>([])
 6const page = ref(1)
 7
 8useInfiniteScroll(listRef, async () => {
 9  const res = await fetch(`/api/items?page=${page.value}`)
10  const newItems = await res.json()
11  items.value.push(...newItems)
12  page.value++
13}, {
14  distance: 200,          // 距離底部 200px 時觸發
15  canLoadMore: () => page.value <= 10,  // 最多載入 10 頁
16})
17</script>

場景 3:表單防抖搜尋

 1<script setup lang="ts">
 2import { refDebounced } from '@vueuse/shared'
 3import { useFetch } from '@vueuse/core'
 4
 5const keyword = ref('')
 6const debouncedKeyword = refDebounced(keyword, 300)
 7
 8const url = computed(() =>
 9  debouncedKeyword.value
10    ? `https://api.example.com/search?q=${debouncedKeyword.value}`
11    : ''
12)
13
14const { data: results, isFetching } = useFetch(url, { refetch: true }).json()
15</script>
16
17<template>
18  <input v-model="keyword" placeholder="搜尋..." />
19  <div v-if="isFetching">搜尋中...</div>
20  <ul v-else>
21    <li v-for="item in results" :key="item.id">{{ item.name }}</li>
22  </ul>
23</template>

場景 4:剪貼簿操作

 1<script setup lang="ts">
 2import { useClipboard } from '@vueuse/core'
 3
 4const { copy, copied, isSupported } = useClipboard({ legacy: true })
 5
 6async function copyCode(code: string) {
 7  await copy(code)
 8  // copied.value 會在複製後短暫變為 true(預設 1500ms)
 9}
10</script>
11
12<template>
13  <button @click="copyCode('npm i @vueuse/core')" :disabled="!isSupported">
14    {{ copied ? '已複製!' : '複製安裝指令' }}
15  </button>
16</template>

場景 5:元素尺寸與位置追蹤

 1<script setup lang="ts">
 2import { useElementSize, useElementBounding, useResizeObserver } from '@vueuse/core'
 3
 4const boxRef = ref<HTMLElement | null>(null)
 5
 6// 追蹤元素尺寸
 7const { width, height } = useElementSize(boxRef)
 8
 9// 追蹤元素邊界(包含位置)
10const { top, left, right, bottom } = useElementBounding(boxRef)
11
12// 或使用 ResizeObserver 做更精細的控制
13useResizeObserver(boxRef, (entries) => {
14  const entry = entries[0]
15  console.log('新尺寸:', entry.contentRect)
16})
17</script>

6. 資安掃描報告

掃描範圍

packages/ 目錄進行全面掃描,檢查以下模式:evalexecos.systemsubprocessshell=Truesecrettokenpasswordapi_keyinnerHTML.cookie

掃描結果

類別發現數量風險等級說明
innerHTML8 處全部位於測試檔案 (.browser.test.ts),用於 document.body.innerHTML = '' 清理 DOM,非生產程式碼
token10 處全部為文件範例或 JWT 整合測試中的測試用 token 字串,無真實憑證外洩
.cookie3 處useCookies 整合模組的正常功能實作,透過 universal-cookie 套件讀寫 cookie
exec3 處useCountdown 測試中的 helper 函式名稱 exec(),非 eval/exec 危險呼叫

結論

風險等級:低(安全)

  • 未發現硬編碼的 API Key、密碼或機密資訊
  • 未發現 eval() 或動態程式碼執行
  • 未發現不安全的 shell 命令執行
  • innerHTML 使用僅限於測試環境的 DOM 清理
  • token 相關發現均為功能性程式碼(JWT 解析)或文件範例
  • 專案有 GitHub Actions 安全設定(pin actions to SHA)

7. FAQ

Q1:VueUse 支援 Vue 2 嗎?

VueUse v10+ 僅支援 Vue 3。若需 Vue 2 支援,請使用 @vueuse/core@9 搭配 @vue/composition-api。但建議儘早升級至 Vue 3。

Q2:在 Nuxt 3 中如何使用?

安裝 @vueuse/nuxt 後在 nuxt.config.ts 加入模組即可,所有函式自動可用:

1export default defineNuxtConfig({
2  modules: ['@vueuse/nuxt'],
3})

Q3:Tree-shaking 是否正常運作?

是的。VueUse 的每個函式都是獨立的 ES module export,現代打包工具(Vite、webpack 5、Rollup)可以自動移除未使用的函式。可透過 packages/export-size.json 檢視每個函式的匯出大小。

Q4:如何在 SSR 環境中使用?

VueUse 的所有核心函式都對 SSR 安全。它們透過 defaultWindow 抽象層偵測環境,在 SSR 時不會存取 windowdocument。若需自訂 SSR 行為,可使用 getSSRHandler / setSSRHandler

Q5:如何自訂 composable 的 event filter?

大多數接受 eventFilter 選項的 composable 支援 throttleFilterdebounceFilter

1import { useMouse, throttleFilter } from '@vueuse/core'
2
3const { x, y } = useMouse({
4  eventFilter: throttleFilter(100), // 每 100ms 最多更新一次
5})

Q6:如何處理 composable 的副作用清理?

VueUse 的 composable 會自動在 onScopeDispose 時清理副作用。若需手動清理:

1const { stop } = useEventListener(window, 'resize', handler)
2stop() // 手動停止

Q7:VueUse 與 Pinia 有衝突嗎?

沒有衝突。VueUse 專注於可組合函式(composables),Pinia 專注於全域狀態管理。兩者可以在同一個專案中共用,例如在 Pinia store 中使用 useLocalStorage 做持久化。

8. 進階技巧

8.1 createGlobalState — 輕量全域狀態

不需要 Pinia 的小型應用可使用 createGlobalState 建立跨元件共享狀態:

 1// stores/user.ts
 2import { createGlobalState, useLocalStorage } from '@vueuse/core'
 3
 4export const useUserStore = createGlobalState(() => {
 5  const user = useLocalStorage<{ name: string; email: string } | null>('user', null)
 6  const isLoggedIn = computed(() => user.value !== null)
 7
 8  function login(name: string, email: string) {
 9    user.value = { name, email }
10  }
11
12  function logout() {
13    user.value = null
14  }
15
16  return { user, isLoggedIn, login, logout }
17})

8.2 createSharedComposable — 共享副作用

確保一個 composable 的副作用只執行一次(多個元件共享同一個實例):

1import { createSharedComposable, useMouse } from '@vueuse/core'
2
3// 無論多少元件使用,只會建立一個 mousemove 監聽器
4const useSharedMouse = createSharedComposable(useMouse)

8.3 watchPausable — 可暫停的 watch

 1import { watchPausable } from '@vueuse/shared'
 2
 3const source = ref(0)
 4
 5const { pause, resume, isActive } = watchPausable(source, (val) => {
 6  console.log('changed to', val)
 7})
 8
 9pause()   // 暫停監聽
10source.value++ // 不會觸發 callback
11resume()  // 恢復監聯

8.4 組合多個 composables

 1// composables/useMouseParallax.ts
 2import { useMouse, useWindowSize, useRafFn } from '@vueuse/core'
 3
 4export function useMouseParallax(factor = 0.05) {
 5  const { x, y } = useMouse()
 6  const { width, height } = useWindowSize()
 7
 8  const offsetX = computed(() => (x.value - width.value / 2) * factor)
 9  const offsetY = computed(() => (y.value - height.value / 2) * factor)
10
11  return { offsetX, offsetY }
12}

8.5 useRefHistory — 操作歷史(undo/redo)

 1import { useRefHistory } from '@vueuse/core'
 2
 3const text = ref('Hello')
 4
 5const { history, undo, redo, canUndo, canRedo } = useRefHistory(text, {
 6  capacity: 50,  // 最多記錄 50 步
 7  deep: true,    // 深層追蹤(物件/陣列)
 8})
 9
10text.value = 'Hello World'
11text.value = 'Hello VueUse'
12
13undo() // text.value === 'Hello World'
14redo() // text.value === 'Hello VueUse'

8.6 自訂 SSR Handler

 1import { setSSRHandler } from '@vueuse/core'
 2
 3// 在 SSR 環境中提供自訂的 storage 實作
 4setSSRHandler('getDefaultStorage', () => {
 5  return {
 6    getItem(key) { /* ... */ },
 7    setItem(key, value) { /* ... */ },
 8    removeItem(key) { /* ... */ },
 9  }
10})

9. 整合進其他工作流

與 Pinia 整合

 1// stores/settings.ts
 2import { defineStore } from 'pinia'
 3import { useLocalStorage, usePreferredDark } from '@vueuse/core'
 4
 5export const useSettingsStore = defineStore('settings', () => {
 6  const theme = useLocalStorage('theme', 'auto')
 7  const systemDark = usePreferredDark()
 8
 9  const isDark = computed(() =>
10    theme.value === 'auto' ? systemDark.value : theme.value === 'dark'
11  )
12
13  return { theme, isDark }
14})

與 Vue Router 整合

1import { useRouteQuery } from '@vueuse/router'
2
3// 雙向綁定 URL query 參數
4const page = useRouteQuery('page', '1', { transform: Number })
5const sort = useRouteQuery('sort', 'date')

與 TanStack Query 整合

 1import { useQuery } from '@tanstack/vue-query'
 2import { refDebounced } from '@vueuse/shared'
 3
 4const search = ref('')
 5const debouncedSearch = refDebounced(search, 300)
 6
 7const { data } = useQuery({
 8  queryKey: ['search', debouncedSearch],
 9  queryFn: () => api.search(debouncedSearch.value),
10  enabled: computed(() => debouncedSearch.value.length > 2),
11})

在元件庫中使用

1// 建立跨框架的 composable 元件
2import { useVModel } from '@vueuse/core'
3
4const props = defineProps<{ modelValue: string }>()
5const emit = defineEmits<{ 'update:modelValue': [value: string] }>()
6
7// 簡化 v-model 雙向綁定
8const value = useVModel(props, 'modelValue', emit)

10. 重點摘要 Checklist


graph LR
    A["安裝
pnpm add @vueuse/core"] --> B["引入
import { useXxx }"] B --> C["使用
const { ... } = useXxx()"] C --> D["自動清理
onScopeDispose"] style A fill:#42b883,color:#fff style B fill:#35495e,color:#fff style C fill:#1e8a7a,color:#fff style D fill:#a1b858,color:#000
  • 安裝pnpm add @vueuse/core(Nuxt 加 @vueuse/nuxt
  • 按需引入:從 @vueuse/core@vueuse/shared 引入需要的函式
  • 響應式輸入:利用 MaybeRefOrGetter 傳入靜態值或 ref
  • 解構使用:所有 composable 回傳物件,可直接解構
  • 副作用清理:composable 自動在 scope dispose 時清理,必要時使用 stop()
  • SSR 安全:所有核心函式 SSR 安全,無需額外處理
  • TypeScript:充分利用型別推斷,享受完整的開發體驗
  • Tree-shaking:確保打包工具啟用 tree-shaking(Vite 預設啟用)
  • 效能:使用 throttleFilter / debounceFilter 控制高頻更新
  • 全域狀態:小型應用使用 createGlobalState 取代 Pinia
  • 歷史管理:需要 undo/redo 時使用 useRefHistory
  • 暗色模式:使用 useDark + useToggle 快速實作

11. 進一步閱讀

官方資源

相關專案

學習資源