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
與類似專案的比較
| 特性 | VueUse | React Hooks (ahooks) | Solid Primitives |
|---|---|---|---|
| 框架 | Vue 3 | React | Solid.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 遵循一致的設計模式:
- 輸入:接受
MaybeRefOrGetter<T>型別的參數,同時支援靜態值、ref 和 getter - 輸出:回傳包含 reactive refs 的物件(解構友善)
- 生命週期:自動在 component unmount 時清理副作用(event listeners、timers 等)
- SSR 安全:透過
defaultWindow/defaultDocument抽象,避免在 SSR 環境存取瀏覽器 API - 可配置性:透過 options 物件提供細緻的控制(如
throttle、eventFilter、window)
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 可歸類為以下幾大領域:
| 分類 | 代表函式 | 說明 |
|---|---|---|
| Browser | useClipboard, useFetch, useStorage, useTitle | 瀏覽器 API 封裝 |
| Sensors | useMouse, useGeolocation, useDeviceMotion | 感測器與輸入裝置 |
| State | useLocalStorage, useSessionStorage, useRefHistory | 狀態管理與持久化 |
| Elements | useElementSize, useIntersectionObserver, useDraggable | DOM 元素操作 |
| Component | useVModel, createReusableTemplate, templateRef | 元件工具 |
| Watch | watchDebounced, watchThrottled, watchPausable | 進階 watch |
| Network | useWebSocket, useEventSource, useOnline | 網路通訊 |
| Animation | useTransition, useRafFn, useAnimate | 動畫與計時 |
| Utilities | useDark, 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/ 目錄進行全面掃描,檢查以下模式:eval、exec、os.system、subprocess、shell=True、secret、token、password、api_key、innerHTML、.cookie。
掃描結果
| 類別 | 發現數量 | 風險等級 | 說明 |
|---|---|---|---|
innerHTML | 8 處 | 低 | 全部位於測試檔案 (.browser.test.ts),用於 document.body.innerHTML = '' 清理 DOM,非生產程式碼 |
token | 10 處 | 無 | 全部為文件範例或 JWT 整合測試中的測試用 token 字串,無真實憑證外洩 |
.cookie | 3 處 | 無 | useCookies 整合模組的正常功能實作,透過 universal-cookie 套件讀寫 cookie |
exec | 3 處 | 無 | 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 時不會存取 window 或 document。若需自訂 SSR 行為,可使用 getSSRHandler / setSSRHandler。
Q5:如何自訂 composable 的 event filter?
大多數接受 eventFilter 選項的 composable 支援 throttleFilter 和 debounceFilter:
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. 進一步閱讀
官方資源
- VueUse 官方文件 — 完整 API 文件與互動式 demo
- GitHub 儲存庫 — 原始碼與 Issue 追蹤
- VueUse 函式列表 — 依分類瀏覽所有函式
- VueUse 貢獻指南 — 如何貢獻新的 composable
相關專案
- Vue 3 — Vue.js 官方文件
- Vite — 下一代前端建構工具
- Nuxt 3 — Vue 全端框架
- Pinia — Vue 官方狀態管理
- Vue Router — Vue 官方路由
- TanStack Query — 非同步狀態管理
學習資源
- Anthony Fu 的部落格 — VueUse 作者的技術文章
- Vue.js 設計與實現 — 深入理解 Vue 3 響應式系統
- VueUse Changelog — 版本更新紀錄
Comments