1
0
mirror of synced 2026-05-20 17:28:31 +08:00

feat(cook): 支持菜谱数据分片加载与增量更新

- CLI 转换命令新增 --chunk-size 参数,支持将菜谱数据拆分为多个分片文件
- 前端 DB 初始化重构,支持单文件和分片两种模式
- 分片模式下通过 hash 比对实现增量更新,减少重复数据加载
- .gitignore 新增 recipe-meta.json 和 recipe-chunk-*.json 忽略规则
- 升级 vite、vitest 等依赖
This commit is contained in:
YunYouJun
2026-03-28 23:12:23 +08:00
parent 0ee8941115
commit 7b4c1a6029
9 changed files with 484 additions and 309 deletions

View File

@@ -2,7 +2,7 @@ export const appName = '食用手册'
export const appDescription = '好的,今天我们来做菜!'
export const namespace = 'cook'
export const lastDbUpdated = '2026-3-25 02:52:00'
export const lastDbUpdated = '2026-3-26 16:45:17'
export const icp = '苏ICP备17038157号'

View File

@@ -7,6 +7,28 @@ export interface DbRecipeItem extends RecipeItem {
id?: number
}
interface RecipeMetaBase {
total: number
version: number
}
interface RecipeMetaSingle extends RecipeMetaBase {
chunked: false
}
interface RecipeMetaChunked extends RecipeMetaBase {
chunked: true
chunkSize: number
chunks: { index: number, hash: string, count: number }[]
}
type RecipeMeta = RecipeMetaSingle | RecipeMetaChunked
const CHUNK_HASH_STORAGE_KEY = 'cook:chunk-hashes'
// 预注册分片文件Vite 会在构建时处理这些 glob 导入
const chunkModules = import.meta.glob<{ default: RecipeItem[] }>('../data/recipe-chunk-*.json')
export class MySubClassedDexie extends Dexie {
recipes!: Table<DbRecipeItem>
@@ -20,7 +42,30 @@ export class MySubClassedDexie extends Dexie {
export const db = new MySubClassedDexie()
export async function initDb() {
/**
* 加载已记录的分片 hash用于增量更新判断
*/
function loadChunkHashes(): Record<string, string> {
try {
const raw = localStorage.getItem(CHUNK_HASH_STORAGE_KEY)
return raw ? JSON.parse(raw) : {}
}
catch {
return {}
}
}
/**
* 保存分片 hash 记录
*/
function saveChunkHashes(hashes: Record<string, string>) {
localStorage.setItem(CHUNK_HASH_STORAGE_KEY, JSON.stringify(hashes))
}
/**
* 单文件模式:全量加载 recipe.json
*/
async function loadSingleFile() {
const { default: recipeData } = await import('../data/recipe.json')
return db.recipes.bulkPut(
@@ -30,3 +75,57 @@ export async function initDb() {
})),
)
}
/**
* 分片模式:增量加载变化的分片
*/
async function loadChunks(meta: RecipeMetaChunked) {
const savedHashes = loadChunkHashes()
const newHashes = { ...savedHashes }
// 找出需要加载的分片hash 变化或新增的)
const chunksToLoad = meta.chunks.filter(
chunk => savedHashes[String(chunk.index)] !== chunk.hash,
)
if (chunksToLoad.length === 0) {
return
}
// 并行加载所有需要更新的分片
await Promise.all(
chunksToLoad.map(async (chunk) => {
const modulePath = `../data/recipe-chunk-${chunk.index}.json`
const loader = chunkModules[modulePath]
if (!loader) {
console.warn(`Chunk module not found: ${modulePath}`)
return
}
const { default: chunkData } = await loader()
const offset = chunk.index * meta.chunkSize
await db.recipes.bulkPut(
(chunkData as RecipeItem[]).map((item, i) => ({
id: offset + i,
...item,
})),
)
newHashes[String(chunk.index)] = chunk.hash
}),
)
saveChunkHashes(newHashes)
}
export async function initDb() {
const { default: meta } = await import('../data/recipe-meta.json') as { default: RecipeMeta }
if (meta.chunked) {
return loadChunks(meta)
}
else {
return loadSingleFile()
}
}