Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
在 Vue 3 中撰寫「共用函數」有幾種方式,最常見的是透過 Composable(可組合函式) 的方式來實作。這是 Vue 3 中 Composition API 推出後的一個最佳實踐,用來撰寫可重用的邏輯。
使用 ts(type script) 假設你有一個功能是格式化日期,你可以這樣寫:
src/
└── composables/
└── useDateFormat.ts # or .js// useDateFormat.ts
export function useDateFormat() {
const formatDate = (date: string | Date): string => {
const d = new Date(date)
return d.toLocaleDateString('zh-TW', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
})
}
return { formatDate }
}<script setup lang="ts">
import { useDateFormat } from '@/composables/useDateFormat'
const { formatDate } = useDateFormat()
const today = formatDate(new Date())
</script>
<template>
<p>今天日期:{{ today }}</p>
</template>
可以依功能拆多個檔案,例如:
src/composables/
├── useDateFormat.ts
├── useFetch.ts
├── useAuth.ts
└── useClipboard.ts如果只是一個簡單函式,不用 reactive,可以這樣
// utils/format.ts
export function formatCurrency(num: number): string {
return `$${num.toFixed(2)}`
}然後在元件或其他 composable 裡匯入使用。
| 情境 | 建議方式 |
|---|---|
| 需要用到 reactive、ref、watch、computed 等 | Composable |
| 純邏輯處理,不涉及 Vue 的 reactivity | 純函式放在 utils |
這是一個實用的共用函式範例,用來「在一個陣列中找出某個 ID 的項目」。
idid 相同的項目(或 undefined)useFindById.ts// composables/useFindById.ts
export function useFindById<T extends { id: number | string }>() {
const findById = (array: T[], id: number | string): T | undefined => {
return array.find(item => item.id === id)
}
return { findById }
}這邊使用了泛型 T,讓你可以傳任何型別進來,只要那個型別有 id 欄位。
<script setup lang="ts">
import { useFindById } from '@/composables/useFindById'
const { findById } = useFindById()
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
]
const user = findById(users, 2) // 找到 Bob
</script>
<template>
<p v-if="user">找到的使用者:{{ user.name }}</p>
<p v-else>找不到使用者</p>
</template>
如果想找所有符合某個 ID 的項目(例如有重複 ID 的情況),可以寫一個類似這樣的:
// composables/useFindAllById.ts
export function useFindAllById<T extends { id: number | string }>() {
const findAllById = (array: T[], id: number | string): T[] => {
return array.filter(item => item.id === id)
}
return { findAllById }
}以下是 JavaScript 版本 的「以 id 找出陣列中符合的項目」的共用函式(Composable)範例
// composables/useFindById.js
export function useFindById() {
const findById = (array, id) => {
return array.find(item => item.id === id)
}
return { findById }
}<script setup> 中使用)<script setup>
import { useFindById } from '@/composables/useFindById'
const { findById } = useFindById()
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
]
const user = findById(users, 2)
</script>
<template>
<p v-if="user">找到的使用者:{{ user.name }}</p>
<p v-else>找不到使用者</p>
</template>// composables/useFindAllById.js
export function useFindAllById() {
const findAllById = (array, id) => {
return array.filter(item => item.id === id)
}
return { findAllById }
}以下是將 findById 和 findAllById 兩個共用函數寫在同一個 JS 檔案中的範例
// composables/useFindById.js
export function useFindById() {
const findById = (array, id) => {
return array.find(item => item.id === id)
}
const findAllById = (array, id) => {
return array.filter(item => item.id === id)
}
return {
findById,
findAllById,
}
}<script setup>
import { useFindById } from '@/composables/useFindById'
const { findById, findAllById } = useFindById()
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 2, name: 'Bobby' },
]
const singleUser = findById(users, 2) // 找到第一個 id 為 2 的使用者
const multipleUsers = findAllById(users, 2) // 找到所有 id 為 2 的使用者
</script>
<template>
<p>第一位符合的使用者:{{ singleUser?.name }}</p>
<p>所有符合的使用者:</p>
<ul>
<li v-for="u in multipleUsers" :key="u.name">{{ u.name }}</li>
</ul>
</template>不只找 id,也能找像是 user_id、slug、code 等等欄位。
更新後的 useFindByKey.js
// composables/useFindByKey.js
export function useFindByKey() {
const findByKey = (array, key, value) => {
return array.find(item => item[key] === value)
}
const findAllByKey = (array, key, value) => {
return array.filter(item => item[key] === value)
}
return {
findByKey,
findAllByKey,
}
}<script setup>
import { useFindByKey } from '@/composables/useFindByKey'
const { findByKey, findAllByKey } = useFindByKey()
const users = [
{ user_id: 1, name: 'Alice' },
{ user_id: 2, name: 'Bob' },
{ user_id: 2, name: 'Bobby' },
]
const user = findByKey(users, 'user_id', 2) // 找第一個 user_id 為 2 的人
const allUsers = findAllByKey(users, 'user_id', 2) // 找所有 user_id 為 2 的人
</script>
<template>
<p>第一位符合的使用者:{{ user?.name }}</p>
<p>所有符合的使用者:</p>
<ul>
<li v-for="u in allUsers" :key="u.name">{{ u.name }}</li>
</ul>
</template>useArrayTools.js支援 key/value 查找、包含/不包含、多值查找等,這個工具會包含以下幾個實用函數:
| 函數名稱 | 說明 |
|---|---|
findByKey | 找到第一個符合 key/value 的項目 |
findAllByKey | 找到所有符合 key/value 的項目 |
findIncludes | 找出 key 包含某字串的項目(適合模糊搜尋) |
findByKeys | 支援多個欄位條件(AND 條件) |
excludeByKey | 排除符合某個 key/value 的項目 |
uniqueByKey | 根據 key 去除重複項目 |
// composables/useArrayTools.js
export function useArrayTools() {
const findByKey = (array, key, value) => {
return array.find(item => item[key] === value)
}
const findAllByKey = (array, key, value) => {
return array.filter(item => item[key] === value)
}
const findIncludes = (array, key, keyword) => {
return array.filter(item => {
const target = item[key]
return typeof target === 'string' && target.includes(keyword)
})
}
const findByKeys = (array, conditions) => {
return array.filter(item =>
Object.entries(conditions).every(([key, value]) => item[key] === value)
)
}
const excludeByKey = (array, key, value) => {
return array.filter(item => item[key] !== value)
}
const uniqueByKey = (array, key) => {
const seen = new Set()
return array.filter(item => {
if (seen.has(item[key])) return false
seen.add(item[key])
return true
})
}
return {
findByKey,
findAllByKey,
findIncludes,
findByKeys,
excludeByKey,
uniqueByKey,
}
}<script setup>
import { useArrayTools } from '@/composables/useArrayTools'
const {
findByKey,
findAllByKey,
findIncludes,
findByKeys,
excludeByKey,
uniqueByKey,
} = useArrayTools()
const users = [
{ user_id: 1, name: 'Alice', role: 'admin' },
{ user_id: 2, name: 'Bob', role: 'editor' },
{ user_id: 2, name: 'Bobby', role: 'editor' },
{ user_id: 3, name: 'Charlie', role: 'viewer' },
]
// 單一欄位查找
const firstEditor = findByKey(users, 'role', 'editor')
// 多筆查找
const allEditors = findAllByKey(users, 'role', 'editor')
// 模糊搜尋
const nameContainsB = findIncludes(users, 'name', 'B')
// 多條件查找
const result = findByKeys(users, { user_id: 2, role: 'editor' })
// 排除某角色
const notAdmin = excludeByKey(users, 'role', 'admin')
// 去除重複 user_id
const uniqueUsers = uniqueByKey(users, 'user_id')
</script>這版除了查找、過濾、去重之外,會加入以下進階功能:
功能新增一覽:
| 函數名稱 | 說明 |
|---|---|
✅ sortByKey | 根據 key 排序,支援升冪/降冪 |
✅ groupByKey | 根據 key 分組,回傳物件結構 |
✅ chunk | 將陣列分割成固定大小的小陣列 |
✅ paginate | 分頁工具(指定每頁筆數與第幾頁) |
useArrayTools.js// composables/useArrayTools.js
export function useArrayTools() {
// === 查找與篩選 ===
// 找符合 key/value 的項目在陣列的第幾筆
const findIndexByKey = (array, key, value) => {
let index = -1;
for (let i = 0; i < array.length; i++) {
if (array[i][key] === value) {
index = i;
break;
}
}
return index;
};
const findByKey = (array, key, value) =>
array.find(item => item[key] === value)
const findAllByKey = (array, key, value) =>
array.filter(item => item[key] === value)
const findIncludes = (array, key, keyword) =>
array.filter(item => {
const target = item[key]
return typeof target === 'string' && target.includes(keyword)
})
const findByKeys = (array, conditions) =>
array.filter(item =>
Object.entries(conditions).every(([key, value]) => item[key] === value)
)
const excludeByKey = (array, key, value) =>
array.filter(item => item[key] !== value)
const uniqueByKey = (array, key) => {
const seen = new Set()
return array.filter(item => {
if (seen.has(item[key])) return false
seen.add(item[key])
return true
})
}
// === 排序 ===
const sortByKey = (array, key, order = 'asc') => {
return [...array].sort((a, b) => {
const aVal = a[key]
const bVal = b[key]
if (aVal < bVal) return order === 'asc' ? -1 : 1
if (aVal > bVal) return order === 'asc' ? 1 : -1
return 0
})
}
// === 分組 ===
const groupByKey = (array, key) => {
return array.reduce((groups, item) => {
const groupKey = item[key]
if (!groups[groupKey]) {
groups[groupKey] = []
}
groups[groupKey].push(item)
return groups
}, {})
}
// === 分割 ===
const chunk = (array, size) => {
const chunks = []
for (let i = 0; i < array.length; i += size) {
chunks.push(array.slice(i, i + size))
}
return chunks
}
// === 分頁 ===
const paginate = (array, page = 1, perPage = 10) => {
const start = (page - 1) * perPage
return array.slice(start, start + perPage)
}
return {
findIndexByKey,
findByKey,
findAllByKey,
findIncludes,
findByKeys,
excludeByKey,
uniqueByKey,
sortByKey,
groupByKey,
chunk,
paginate,
}
}<script setup>
import { useArrayTools } from '@/composables/useArrayTools'
const {
sortByKey,
groupByKey,
chunk,
paginate
} = useArrayTools()
const users = [
{ id: 1, name: 'Alice', role: 'admin' },
{ id: 2, name: 'Bob', role: 'editor' },
{ id: 3, name: 'Carol', role: 'editor' },
{ id: 4, name: 'David', role: 'viewer' },
{ id: 5, name: 'Eve', role: 'editor' },
]
// 排序(依照 name 升冪)
const sorted = sortByKey(users, 'name')
// 分組(依照 role)
const grouped = groupByKey(users, 'role')
// 分塊(每 2 筆一組)
const chunks = chunk(users, 2)
// 分頁(第 2 頁,每頁 2 筆)
const page2 = paginate(users, 2, 2)
</script>