feat: 优化同步数据结构 — 来源追溯 + 动态回填,减少 Supabase 存储 80%#263
Open
didyhu wants to merge 3 commits into
Open
Conversation
…ation Replace full-copy word storage with a reference + backfill model: - Add sourceDictId to Word type to trace word origin - Auto-inject sourceDictId when collecting, marking wrong, or marking known - Intelligently slim words before sync: extreme slimming for words with sourceDictId (details stripped), balanced for legacy/custom words - New useWordHydrator composable rehydrates stripped words on display from locally available dictionaries - When dict not available, show clickable placeholder "下载 XX 查看释义" instead of auto-downloading full dict JSON from CDN Closes zyronon#260
There was a problem hiding this comment.
Pull request overview
该 PR 通过“来源追溯 + 同步瘦身 + 展示时动态回填”的方式,减少 Supabase 同步数据中三类特殊词库(收藏/错词/已掌握)对完整 Word 详情的冗余存储,从而显著降低同步 payload 体积并保持可用性。
Changes:
- 为
Word增加sourceDictId,并在收藏/错词/已掌握写入时自动注入来源词典 ID。 - 调整
shakeCommonDict():对特殊词库按是否有来源进行分级瘦身(有来源则极致瘦身,无来源则平衡瘦身)。 - 新增
useWordHydrator与WordItem端的回填触发/缺失提示,引导用户下载对应词典查看释义。
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/core/src/utils/index.ts | 对特殊词库做分级瘦身以降低同步体积 |
| packages/core/src/types/types.ts | Word 类型新增 sourceDictId 用于来源追溯 |
| packages/core/src/hooks/useWordHydrator.ts | 新增动态回填逻辑,从本地词典按需补全详情 |
| packages/core/src/hooks/dict.ts | 收藏/已掌握操作时注入 sourceDictId |
| packages/core/src/components/word/WordItem.vue | 列表项触发回填并在缺词典时展示跳转提示 |
| apps/nuxt/app/pages/(words)/practice-words/[id].vue | 记录错词时注入 sourceDictId |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+4
to
+6
| const dictCache: Record<string, Word[]> = {} | ||
| const pendingRequests: Record<string, Promise<Word[]>> = {} | ||
|
|
Comment on lines
+4
to
+6
| const dictCache: Record<string, Word[]> = {} | ||
| const pendingRequests: Record<string, Promise<Word[]>> = {} | ||
|
|
Comment on lines
+49
to
+50
| Object.assign(word, details) | ||
| } |
Comment on lines
+56
to
+67
| function goDictList() { | ||
| router.push('/dict-list') | ||
| } | ||
|
|
||
| onMounted(() => { | ||
| doHydrate(props.item) | ||
| }) | ||
|
|
||
| watch( | ||
| () => props.item, | ||
| val => { | ||
| doHydrate(val) |
Comment on lines
+46
to
+69
| async function doHydrate(item: Word) { | ||
| const result = await hydrate(item) | ||
| if (!result.hydrated && result.dictName) { | ||
| hydrateFailed = true | ||
| missingDictName = result.dictName | ||
| } else if (result.hydrated) { | ||
| hydrateFailed = false | ||
| } | ||
| } | ||
|
|
||
| function goDictList() { | ||
| router.push('/dict-list') | ||
| } | ||
|
|
||
| onMounted(() => { | ||
| doHydrate(props.item) | ||
| }) | ||
|
|
||
| watch( | ||
| () => props.item, | ||
| val => { | ||
| doHydrate(val) | ||
| } | ||
| ) |
Comment on lines
+83
to
+86
| <TranslationList :pos-space="false" :word="item" :showFull="showWord" v-if="showTranslate && !hydrateFailed" /> | ||
| <div v-else-if="showTranslate && hydrateFailed" class="missing-dict-hint" @click="goDictList"> | ||
| 下载 {{ missingDictName }} 查看释义 | ||
| </div> |
Comment on lines
+21
to
+47
| const dictId = word.sourceDictId | ||
| let wordsData: Word[] = [] | ||
|
|
||
| if (dictCache[dictId]) { | ||
| wordsData = dictCache[dictId] | ||
| } else if (pendingRequests[dictId]) { | ||
| wordsData = await pendingRequests[dictId] | ||
| } else { | ||
| // 检查用户已添加的词典中是否有已加载的单词数据 | ||
| const store = useBaseStore() | ||
| const userDict = store.word.bookList.find(v => v.id === dictId) | ||
|
|
||
| if (userDict?.words?.length) { | ||
| dictCache[dictId] = userDict.words | ||
| wordsData = userDict.words | ||
| } else { | ||
| // 词典数据不可用:不自动下载,返回失败状态 | ||
| const dictName = userDict?.name || dictId | ||
| return { hydrated: false, dictName } | ||
| } | ||
| } | ||
|
|
||
| if (wordsData.length > 0) { | ||
| const sourceWord = wordsData.find( | ||
| w => w.word.toLowerCase() === word.word.toLowerCase() | ||
| ) | ||
| if (sourceWord) { |
- Replace dictCache with indexed Map per dictId (O(1) lookup), validate dict still exists in bookList on each hydrate to prevent stale cache - Remove dead pendingRequests variable - Return hydrated=false when sourceWord not found in dictionary - Skip hydration when showTranslate is false - Add incrementing token to prevent race condition in async hydration - Change missing-dict-hint from <div> to <button> for accessibility
|
@didyhu is attempting to deploy a commit to the zyronon's projects Team on Vercel. A member of the Team first needs to authorize it. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
概述
将特殊词库(收藏、错词、已掌握)的存储模型从"全量副本"改为"引用 + 按需回填"。
实测单行 JSON 体积可从 2.36MB 降至 ~0.5MB,降幅约 80%。
方案
涉及文件
兼容性
测试
Closes #260