Use when 審計 Nuxt 專案 data-fetching 模式與效能 golden path checklist。NOT for 非 Nuxt 專案,NOT for client-server 分頁參數不一致的偵測(走 data-sanity)。
Scanned 9/5/2026
Install to Claude Code
npx -y skills add Charles5277/nuxt-supabase-starter --skill nuxt-data-audit --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Nuxt Data Audit?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/charles5277-nuxt-data-audit)More formats (shields.io, HTML) on the badges page.
---
name: nuxt-data-audit
description: "Use when 審計 Nuxt 專案 data-fetching 模式與效能 golden path checklist。NOT for 非 Nuxt 專案,NOT for client-server 分頁參數不一致的偵測(走 data-sanity)。"
effort: medium
metadata:
author: clade
version: "1.0"
permission_tier: read-only
---
<!-- 🔒 LOCKED — managed by clade · auto-generated by sync-to-cursor; edit source in .claude/ then re-run sync -->
`/nuxt-data-audit` — Nuxt data-fetching & performance golden path audit.
Reference rule:`rules/core/nuxt-data-perf.md`
Cookbook:`~/offline/clade/vendor/snippets/nuxt-data-perf/`
## 何時跑
- **定期稽核**:跨 consumer fleet 完善度掃描(從 clade home 跑,指定 consumer path)
- **新功能完成後**:對 consumer 當前 codebase 的 data-fetching 品質做 baseline check
- **code-review 輔助**:reviewer 可跑此 skill 取得量化數據輔助 review
- **新 consumer onboard**:day-1 baseline 建立
## 怎麼跑
```
/nuxt-data-audit # 掃當前 cwd 的 consumer
/nuxt-data-audit ~/offline/TDMS # 掃指定 consumer
/nuxt-data-audit --fleet # 掃全 fleet(從 clade home 用 registry/consumers.json)
```
## Phase 1 — Dependency Detection
判斷 consumer 的 data-fetching stack:
```bash
# 檢查 package.json
grep -E '@pinia/colada|@pinia/colada-nuxt|@pinia/nuxt' package.json
```
分為兩類:
- **Colada consumer**:安裝了 @pinia/colada → 全部 checklist 適用
- **Non-Colada consumer**:未安裝 → E9/E10 標 N/A,其餘全部適用
## Phase 2 — Automated Scan
對每項 checklist 跑 grep/AST 掃描。以下是每項的掃描方法:
### E1 — setup 無裸 $fetch(HR-1)
```bash
# 找 .vue 檔中 <script setup> 的 $fetch(排除 event handler)
# 注意 $csrfFetch 等 alias 也要查
find . -name '*.vue' -not -path '*/node_modules/*' -not -path '*/.nuxt/*' -not -path '*/test/*' \
| xargs grep -l '\$fetch\|\$csrfFetch'
```
**對每個命中檔案**:讀取 `<script setup>` 區塊,判斷 $fetch 是否在 top-level(fail)還是在 function/handler 內(pass)。Top-level = 不在任何 function/arrow/method 定義內的 await $fetch。
**判定**:
- pass:所有 $fetch 都在 event handler / function 內
- partial:部分在 top-level 但有 useAsyncData 包裹
- fail:有裸 top-level $fetch
### E2 — useFetch 有適當 key(HR-4 useFetch 部分)
```bash
grep -rn 'useFetch\|useAsyncData' --include='*.ts' --include='*.vue' \
| grep -v node_modules | grep -v .nuxt | grep -v test/
```
**判定**:custom composable(`composables/*.ts` / `queries/*.ts`)內的 useFetch 是否手動指定 key。Page/component 內的 useFetch 自動生成 key 通常 OK。
### E3 — 重複請求處理正確(HR-2)
```bash
# 找所有 useFetch 呼叫
grep -rn 'useFetch\|\.refresh(' --include='*.vue' --include='*.ts' \
| grep -v node_modules | grep -v .nuxt
# 檢查有沒有 dedupe
grep -rn "dedupe" --include='*.vue' --include='*.ts' \
| grep -v node_modules | grep -v .nuxt
```
**判定**(**NEVER** 把「0 處 dedupe」直接判 fail——預設 `'cancel'` 對「只要最新結果」的場景本來就是正解):
| 觀察到的情形 | 評分 |
| --- | --- |
| 搜尋框 / 篩選器被加了 `dedupe: 'defer'` | **fail**(引入 bug:回傳舊關鍵字的 promise) |
| key 隨輸入變動的 query 加了 `'defer'` | **fail**(無效程式碼:該分支永遠走不到) |
| 輸入框觸發 fetch 但無 debounce,且舊請求不 abort | **fail**(請求放大) |
| 同一 key 的冪等重複觸發(連點 refresh)未處理 | partial |
| 全部維持預設 `'cancel'`,且無請求放大 | **pass** |
`defer` 只在「同一個 key 有 in-flight request」時生效,逐個 call site 對照 [[nuxt-data-perf]] HR-2 判斷。
### E4 — Reference data 有 cache 策略(HR-3)
```bash
# Colada consumer:檢查 staleTime 使用率
grep -rn 'staleTime' --include='*.ts' --include='*.vue' | grep -v node_modules | grep -v .nuxt
# Non-Colada:檢查 getCachedData
grep -rn 'getCachedData' --include='*.ts' --include='*.vue' | grep -v node_modules | grep -v .nuxt
```
**判定**:staleTime/getCachedData count vs useQuery/useFetch count。比率 > 50% = pass,> 20% = partial,< 20% = fail。
### E5 — 大 payload 有 pick/transform(SR-1)
```bash
grep -rn 'pick:\|transform:' --include='*.ts' --include='*.vue' \
| grep -v node_modules | grep -v .nuxt | grep -v test/ | grep -v nuxt.config | grep -v vite.config
```
**判定**:有 useFetch/useQuery 但 0 pick/transform → fail。有至少一些 → partial。
### E6 — Lazy 元件用對地方且有 hydration strategy(SR-2)
```bash
# 總量
grep -rn '<Lazy' --include='*.vue' | grep -v node_modules | grep -v .nuxt
# 原子元件濫用(首屏輕量元件,淨負面)
grep -roE '<Lazy(UButton|UBadge|UIcon|USeparator|USkeleton|UFormField|UInput|UTextarea|UAlert|UTooltip|UKbd|UAvatar|UChip)\b' \
--include='*.vue' . | grep -v node_modules | grep -v .nuxt | wc -l
# hydration strategy 採用
grep -roE 'hydrate-on-(visible|idle|interaction|media-query)|hydrate-(after|when|never)' \
--include='*.vue' . | grep -v node_modules | grep -v .nuxt | wc -l
```
**判定**(**NEVER** 用「Lazy 用得多 = 好」評分——`<Lazy*>` 只做 code-split,不搭 strategy 完全不省 hydration):
| 條件 | 評分 |
| --- | --- |
| 原子元件濫用比 > 30%,或 hydration strategy = 0 | fail |
| 原子元件濫用比 10–30%,strategy 覆蓋 < 50% 重元件 | partial |
| 原子元件濫用比 < 10%,且重元件多數帶 strategy | pass |
濫用比 = 原子元件 Lazy 數 ÷ 總 Lazy 數。`LazyUSkeleton` 特別標注——loading 指示器本身被放進 network 關鍵路徑,本末倒置。
### E7 — routeRules 有 performance 設定(SR-3)
```bash
grep -A20 'routeRules' nuxt.config.ts | grep -E 'prerender|swr|isr|ssr:\s*false'
```
**判定**:有 prerender/swr/isr = pass。只有 csurf/security = partial。完全沒有 = fail。
### E8 — NuxtImg 最佳化(SR-5)
```bash
grep -rn '<NuxtImg\|<nuxt-img' --include='*.vue' | grep -v node_modules | grep -v .nuxt
# 檢查有沒有 format/loading/priority
grep -rn 'format=.*webp\|loading=.*lazy\|fetchpriority\|:preload=' --include='*.vue' | grep -v node_modules
```
**判定**:@nuxt/image 已安裝 + NuxtImg 有 format/loading → pass。裝了但沒 optimize → partial。沒裝 → N/A(不強制)。
### E9 — Colada key factory(HR-4,Colada only)
```bash
# Key factory pattern
grep -rn 'Keys\s*=\|KEYS\.\|queryKeys\|defineQueryOptions' --include='*.ts' | grep -v node_modules | grep -v .nuxt
# Magic string keys
grep -rn "key:\s*\['" --include='*.ts' --include='*.vue' | grep -v node_modules | grep -v .nuxt | grep -v test/
```
**判定**:factory count > magic string count = pass。反之 = fail。
### E10 — useMutation 有 cache invalidation(HR-5,Colada only)
```bash
mutation_files=$(grep -rl 'useMutation' --include='*.ts' --include='*.vue' | grep -v node_modules | grep -v .nuxt | grep -v test/)
invalidate_files=$(grep -rl 'invalidateQueries' --include='*.ts' --include='*.vue' | grep -v node_modules | grep -v .nuxt | grep -v test/)
```
**判定**:invalidation files >= mutation files = pass。差距 > 30% = fail。
### E11 — 無跨 request state 污染
```bash
grep -rn 'export const.*= ref(\|export const.*= reactive(' --include='*.ts' \
| grep -v node_modules | grep -v .nuxt | grep -v test/ | grep -v defineStore
```
**判定**:0 hits = pass。> 0 = fail(嚴重 — SSR cross-request 污染)。
### E12 — Error handling 完整
對 useFetch/useQuery 呼叫:檢查 `error` ref 是否被 template 消費(`v-if="error"`、`{{ error }}`)或 watch。
**判定**:> 50% 有 error 消費 = pass。< 50% = partial。0 = fail。
### E13 — 平行 request 用 Promise.all
```bash
grep -rn 'Promise.all' --include='*.ts' --include='*.vue' | grep -v node_modules | grep -v .nuxt
```
**判定**:advisory,有就加分。
### E14 — watch/immediate 合理
```bash
grep -rn 'immediate:\s*false\|watch:\s*\[' --include='*.ts' --include='*.vue' \
| grep -v node_modules | grep -v .nuxt | grep -v test/
```
**判定**:有使用 = pass。全部預設 = partial。
### E15 — Nitro 快取有過認證邊界(SR-6)
```bash
grep -rn 'defineCachedEventHandler\|cachedEventHandler\|defineCachedFunction' \
--include='*.ts' server/ | grep -v node_modules
# 對每個命中:檢查同檔有無 auth 取用
grep -n 'getUser\|requireAuth\|serverSupabaseUser\|event.context.user\|getKey' <命中檔>
# 部署目標與 cache driver
grep -n 'preset\|storage' nuxt.config.ts | grep -iE 'cloudflare|storage'
```
**判定**(**安全項,優先於效能**):
- fail:有快取 handler 且該 endpoint 取用使用者身分,但 `getKey()` 未納入使用者識別 → **跨使用者資料外洩**
- fail:部署 Workers / edge 但未設 `storage.cache` driver → 快取實質未生效(memory 不跨 isolate)
- pass:0 個快取 handler(未採用不扣分),或全部為 public 且與身分無關
### E16 — 字型單一來源且明列 weight(SR-7)
```bash
# static 字型 bare import(只載 weight 400)
grep -rn "@import\s*['\"]@fontsource/[^/'\"]*['\"]" --include='*.css' . | grep -v node_modules
# 實際用到的字重
grep -roE 'font-(medium|semibold|bold|black|light|thin)' --include='*.vue' . | grep -v node_modules | sort -u
# 雙重宣告:nuxt.config fonts.families vs CSS @import
grep -n 'families' nuxt.config.ts
```
**判定**:
- fail:有 bare import **且** codebase 用了 400 以外字重 → 合成粗體(CJK faux bold 筆畫糊化)
- fail:`fonts.families` 與 CSS `@import` 宣告同一字型 → 雙重載入
- pass:明列 weight subpath,或只用 `@fontsource-variable/*`(可變字型 bare import 涵蓋全 weight)
### E17 — routeRules 選對渲染模式(SR-8)
```bash
grep -A30 'routeRules' nuxt.config.ts | grep -E 'prerender|isr|swr|cache|ssr'
grep -n 'ssr:\s*false' nuxt.config.ts
```
**判定**:public route 依內容性質選 `prerender` / `isr` / `swr` / `cache`。
> **`experimental.payloadExtraction` NEVER 計入缺口**:Nuxt 4 預設即 `true`,且 `ssr: false` 時強制 `false`。SPA consumer 此項一律 `n/a`。
## Phase 3 — Grading
| Grade | 條件 |
|-------|------|
| A | 全部 HR pass + ≥ 80% SR pass |
| B | 全部 HR pass(SR 可 partial) |
| C | ≥ 3 HR pass,其他 partial |
| D | < 3 HR pass |
| F | E1 或 E11 或 E15 fail(SSR 安全 / 跨使用者資料外洩) |
HR(Hard Rules)= E1, E3, E4 (cache), E9 (key factory), E10 (invalidation), E11 (state safety), **E15 (Nitro 快取認證邊界)**
SR(Should Rules)= E2, E5, E6, E7, E8, E12, E13, E14, E16, E17
> **E15 是安全項不是效能項**:快取套在帶認證的 endpoint 上會把 A 使用者的回應發給 B 使用者。E15 fail 時 grade **一律不高於 F**,與 E1 / E11 同級處理。
## Phase 4 — Report Output
### Single consumer
```
## nuxt-data-audit report: <consumer>
Grade: B+
Stack: Pinia Colada 1.3.1 + useFetch (mixed)
| # | Check | Score | Detail |
|---|-------|-------|--------|
| E1 | setup 無裸 $fetch | ✅ pass | 0 violations |
| E2 | useFetch key | ✅ pass | 3/3 custom composables have explicit key |
| E3 | dedupe | ❌ fail | 0/15 high-freq endpoints have dedupe |
| ... | ... | ... | ... |
### Top 3 Improvements
1. **E3 重複請求** — 2 個搜尋框誤加 dedupe:'defer'(回傳舊關鍵字),3 處輸入框無 debounce [files listed]
2. **E9 key factory** — 7 magic string keys should migrate to factory [files listed]
3. **E5 pick/transform** — 5 list endpoints return full rows [files listed]
```
### Fleet mode(--fleet)
```
## nuxt-data-audit fleet report
| Consumer | Grade | E1 | E2 | E3 | E4 | E5 | E6 | ... |
|----------|-------|----|----|----|----|----|----|-----|
| perno | A- | ✅ | ✅ | ❌ | ✅✅ | ⚠️ | ❌ | ... |
| TDMS | B+ | ⚠️ | ✅ | ❌ | ✅ | ✅ | ✅✅ | ... |
| ... | ... | ... | ... | ... | ... | ... | ... | ... |
### Fleet-wide gaps (priority order)
1. E3 dedupe — 0/7 consumers pass
2. E7 routeRules — 1/7 consumers pass
3. E6 Lazy — 2/7 consumers pass
```
## Enforcement Integration
本 skill 的 rule(`rules/core/nuxt-data-perf.md`)透過 `paths` frontmatter 在觸及 `.vue/.ts` 時自動載入。但 rule 被載入 ≠ rule 被遵守。以下是三層 enforcement:
### Layer 1 — Rule Self-check Gate(寫 code 時)
Rule 內含:**每次寫完新的 useFetch / useQuery / $fetch 呼叫後,MUST 對照 HR-1~HR-5 自查**。這條靠主線的字面遵守生效,沒有機械 gate 接住它——Layer 2 / 3 才是兜底。
### Layer 2 — Code Review Cross-check(review 時)
`/code-review` 對 diff 中新增的 data-fetching 呼叫,SHOULD 跑以下 quick check:
- 新的 useFetch 有 dedupe 嗎?
- 新的 useQuery 有 staleTime 嗎?
- 新的 useMutation 有 invalidateQueries 嗎?
- 新的 $fetch 在 setup top-level 嗎?
### Layer 3 — Periodic Fleet Audit(定期)
從 clade home 跑 `/nuxt-data-audit --fleet`,更新 HANDOFF.md 稽核 baseline。
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!