@lacqjs/nuxt-dict 0.0.8 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/module.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lacqjs/nuxt-dict",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "configKey": "dict",
5
5
  "compatibility": {
6
6
  "nuxt": "^4.4.8"
package/dist/module.mjs CHANGED
@@ -7,7 +7,7 @@ export { createDictTranslator } from '../dist/runtime/utils/dict-translator.js';
7
7
  export { defineDictAdapter } from '../dist/runtime/core/define-adapter.js';
8
8
 
9
9
  const name = "@lacqjs/nuxt-dict";
10
- const version = "0.0.8";
10
+ const version = "0.0.9";
11
11
  const devDependencies = {
12
12
  nuxt: "^4.4.8"};
13
13
  const pkg = {
@@ -1,6 +1,8 @@
1
1
  import { shallowRef, ref, watch, onMounted, onBeforeUnmount, triggerRef } from "vue";
2
2
  import { useNuxtApp } from "#imports";
3
3
  import { DEFAULT_STORE_NAME } from "../core/cache/indexeddb-cache.js";
4
+ import { createLogger } from "../utils/logger.js";
5
+ const logger = createLogger("nuxt-dict");
4
6
  const activeInstances = /* @__PURE__ */ new Map();
5
7
  async function fetchDictData(manager, dictType, storeName, data, loading, error, mode = "load", itemMap) {
6
8
  loading.value = true;
@@ -20,6 +22,7 @@ async function fetchDictData(manager, dictType, storeName, data, loading, error,
20
22
  }
21
23
  }
22
24
  } catch (e) {
25
+ logger.debug(`fetchDictData ERROR store=${storeName} type=${dictType} mode=${mode}`, e);
23
26
  error.value = e instanceof Error ? e.message : String(e);
24
27
  } finally {
25
28
  loading.value = false;
@@ -2,47 +2,70 @@ import type { DictEntry, CacheEntry } from '../../types/index.js';
2
2
  /** 默认 IndexedDB 对象存储库名称 */
3
3
  export declare const DEFAULT_STORE_NAME = "dicts";
4
4
  /**
5
- * IndexedDB 缓存实现,用于持久化字典数据。
6
- * 支持客户端离线访问,减少重复网络请求。
7
- * 版本号改用 localStorage 存储,不再使用 IndexedDB。
5
+ * 基于 Dexie.js 的 IndexedDB 缓存实现(多表方案)。
8
6
  *
9
- * 多对象存储库支持:
10
- * - 默认存储库 `'dicts'` 在 init() 时创建
11
- * - 额外存储库通过 ensureStore() 惰性创建
12
- * - 使用串行升级队列处理并发多 store 创建,批量累积后一次升级完成
7
+ * @description 每个字典仓库对应一个独立的 Dexie table(IndexedDB object store),
8
+ * 通过 init(storeNames) 在初始化时一次性声明所有表的 schema,
9
+ * 避免原生 IndexedDB 多次版本升级导致的竞态问题。
10
+ *
11
+ * 数据隔离:各仓库数据物理隔离,在 DevTools Application 面板中独立可见。
12
+ * Schema 管理:使用仓库数量作为版本号,增删仓库时自动升级。
13
+ *
14
+ * @example
15
+ * const cache = new IndexedDBCache('nuxt-dict')
16
+ * await cache.init(['dicts', 'dicts2', 'dicts3'])
17
+ * await cache.set('dicts', 'gender', 'zh-CN', entry)
18
+ * const result = await cache.get('dicts', 'gender', 'zh-CN')
13
19
  */
14
20
  export declare class IndexedDBCache {
15
- private dbName;
16
21
  private db;
17
- private version;
18
- /** 串行化升级队列,防止并发 ensureStore 导致多次版本升级 */
19
- private upgradeQueue;
20
- /** 累积待创建的 store 名,批量创建以减少升级次数 */
21
- private pendingStores;
22
+ private dbName;
23
+ /**
24
+ * @param {string} dbName - IndexedDB 数据库名称
25
+ */
22
26
  constructor(dbName: string);
23
- /** 初始化 IndexedDB,创建默认对象存储库 */
24
- init(): Promise<void>;
25
27
  /**
26
- * 惰性确保指定名称的对象存储库存在。
27
- * 若当前 DB 中已有该 store 则立即返回;
28
- * 否则将 store 名加入待建集合,通过串行队列触发一次版本升级批量创建。
28
+ * 初始化数据库,一次性声明所有仓库对应的 object store。
29
+ *
30
+ * @description 根据传入的仓库名列表声明 Dexie schema,每个仓库一个独立表。
31
+ * 使用仓库数量作为版本号,增减仓库时自动触发 Dexie 的版本升级。
32
+ * 若版本降级(删了仓库)导致 VersionError,自动删除旧库重建。
33
+ *
34
+ * @param {string[]} storeNames - 所有仓库名列表,如 ['dicts', 'dicts2', 'dicts3']
35
+ * @returns {Promise<void>}
29
36
  */
30
- private ensureStore;
37
+ init(storeNames: string[]): Promise<void>;
31
38
  /**
32
- * 执行一次版本升级,批量创建所有累积的待建 object store。
33
- * 内部注册 onversionchange 确保旧连接能被浏览器主动关闭,
34
- * blocked 事件不视为错误——旧连接释放后 success 事件会自动触发。
39
+ * 生成缓存键。
40
+ * @param {string} dictType - 字典类型,如 'gender'
41
+ * @param {string} locale - 语言标识,如 'zh-CN'
42
+ * @returns {string} 格式 `${dictType}_${locale}`
35
43
  */
36
- private doUpgrade;
37
- /** 生成存储键名:`{dictType}_{locale}` */
38
44
  private getStoreKey;
39
- /** 从指定存储库读取字典缓存 */
45
+ /**
46
+ * 从指定仓库读取字典缓存。
47
+ *
48
+ * @param {string} storeName - 仓库名,如 'dicts'
49
+ * @param {string} dictType - 字典类型,如 'gender'
50
+ * @param {string} locale - 语言标识,如 'zh-CN'
51
+ * @returns {Promise<CacheEntry<DictEntry> | null>} 缓存条目,未命中返回 null
52
+ */
40
53
  get(storeName: string, dictType: string, locale: string): Promise<CacheEntry<DictEntry> | null>;
41
- /** 写入字典缓存条目到指定存储库 */
54
+ /**
55
+ * 写入缓存条目到指定仓库。
56
+ *
57
+ * @param {string} storeName - 仓库名
58
+ * @param {string} dictType - 字典类型
59
+ * @param {string} locale - 语言标识
60
+ * @param {CacheEntry<DictEntry>} entry - 缓存条目
61
+ * @returns {Promise<void>}
62
+ */
42
63
  set(storeName: string, dictType: string, locale: string, entry: CacheEntry<DictEntry>): Promise<void>;
43
64
  /**
44
- * 清空字典缓存数据。
45
- * @param storeName 指定要清空的存储库名,不传则清空全部存储库
65
+ * 清空缓存数据。
66
+ *
67
+ * @param {string} [storeName] - 指定仓库名则只清该仓库,不传则清空全部
68
+ * @returns {Promise<void>}
46
69
  */
47
70
  clear(storeName?: string): Promise<void>;
48
71
  }
@@ -1,144 +1,109 @@
1
+ import Dexie from "dexie";
2
+ import { createLogger } from "../../utils/logger.js";
1
3
  export const DEFAULT_STORE_NAME = "dicts";
4
+ const logger = createLogger("nuxt-dict");
2
5
  export class IndexedDBCache {
6
+ db;
3
7
  dbName;
4
- db = null;
5
- version = 1;
6
- /** 串行化升级队列,防止并发 ensureStore 导致多次版本升级 */
7
- upgradeQueue = Promise.resolve();
8
- /** 累积待创建的 store 名,批量创建以减少升级次数 */
9
- pendingStores = /* @__PURE__ */ new Set();
8
+ /**
9
+ * @param {string} dbName - IndexedDB 数据库名称
10
+ */
10
11
  constructor(dbName) {
11
12
  this.dbName = dbName;
12
- }
13
- /** 初始化 IndexedDB,创建默认对象存储库 */
14
- init() {
15
- if (this.db) return Promise.resolve();
16
- return new Promise((resolve, reject) => {
17
- const request = indexedDB.open(this.dbName);
18
- request.addEventListener("upgradeneeded", () => {
19
- const db = request.result;
20
- if (!db.objectStoreNames.contains(DEFAULT_STORE_NAME)) {
21
- db.createObjectStore(DEFAULT_STORE_NAME);
22
- }
23
- });
24
- request.addEventListener("success", () => {
25
- this.db = request.result;
26
- this.version = this.db.version;
27
- resolve();
28
- });
29
- request.addEventListener("error", () => {
30
- reject(
31
- new Error("Failed to open IndexedDB: " + (request.error?.message || "unknown error"))
32
- );
33
- });
34
- request.addEventListener("blocked", () => {
35
- reject(new Error("IndexedDB is blocked by another tab"));
36
- });
37
- });
13
+ this.db = new Dexie(dbName);
38
14
  }
39
15
  /**
40
- * 惰性确保指定名称的对象存储库存在。
41
- * 若当前 DB 中已有该 store 则立即返回;
42
- * 否则将 store 名加入待建集合,通过串行队列触发一次版本升级批量创建。
16
+ * 初始化数据库,一次性声明所有仓库对应的 object store。
17
+ *
18
+ * @description 根据传入的仓库名列表声明 Dexie schema,每个仓库一个独立表。
19
+ * 使用仓库数量作为版本号,增减仓库时自动触发 Dexie 的版本升级。
20
+ * 若版本降级(删了仓库)导致 VersionError,自动删除旧库重建。
21
+ *
22
+ * @param {string[]} storeNames - 所有仓库名列表,如 ['dicts', 'dicts2', 'dicts3']
23
+ * @returns {Promise<void>}
43
24
  */
44
- async ensureStore(storeName) {
45
- await this.init();
46
- if (this.db.objectStoreNames.contains(storeName)) return;
47
- this.pendingStores.add(storeName);
48
- this.upgradeQueue = this.upgradeQueue.then(() => this.doUpgrade());
49
- return this.upgradeQueue;
25
+ async init(storeNames) {
26
+ const schema = {};
27
+ for (const name of storeNames) {
28
+ schema[name] = "";
29
+ }
30
+ const version = Math.max(storeNames.length, 1);
31
+ this.db.version(version).stores(schema);
32
+ logger.debug(`IndexedDB init: opening "${this.dbName}", version=${version}, stores=[${storeNames.join(", ")}]`);
33
+ try {
34
+ await this.db.open();
35
+ } catch (e) {
36
+ if (e instanceof Error && e.name === "VersionError") {
37
+ logger.debug(`IndexedDB init: VersionError, deleting old database and recreating`);
38
+ this.db.close();
39
+ await Dexie.delete(this.dbName);
40
+ this.db = new Dexie(this.dbName);
41
+ this.db.version(version).stores(schema);
42
+ await this.db.open();
43
+ } else {
44
+ throw e;
45
+ }
46
+ }
47
+ logger.debug(`IndexedDB init: complete, tables=[${this.db.tables.map((t) => t.name).join(", ")}]`);
50
48
  }
51
49
  /**
52
- * 执行一次版本升级,批量创建所有累积的待建 object store。
53
- * 内部注册 onversionchange 确保旧连接能被浏览器主动关闭,
54
- * blocked 事件不视为错误——旧连接释放后 success 事件会自动触发。
50
+ * 生成缓存键。
51
+ * @param {string} dictType - 字典类型,如 'gender'
52
+ * @param {string} locale - 语言标识,如 'zh-CN'
53
+ * @returns {string} 格式 `${dictType}_${locale}`
55
54
  */
56
- async doUpgrade() {
57
- if (this.pendingStores.size === 0) return;
58
- const stores = new Set(this.pendingStores);
59
- this.pendingStores.clear();
60
- const toCreate = [...stores].filter((s) => !this.db.objectStoreNames.contains(s));
61
- if (toCreate.length === 0) return;
62
- this.db.onversionchange = () => {
63
- this.db.close();
64
- };
65
- this.db.close();
66
- this.version++;
67
- await new Promise((resolve, reject) => {
68
- const req = indexedDB.open(this.dbName, this.version);
69
- req.addEventListener("upgradeneeded", () => {
70
- const db = req.result;
71
- for (const name of toCreate) {
72
- if (!db.objectStoreNames.contains(name)) {
73
- db.createObjectStore(name);
74
- }
75
- }
76
- });
77
- req.addEventListener("success", () => {
78
- this.db = req.result;
79
- resolve();
80
- });
81
- req.addEventListener("error", () => reject(req.error));
82
- req.addEventListener("blocked", () => {
83
- });
84
- });
85
- }
86
- /** 生成存储键名:`{dictType}_{locale}` */
87
55
  getStoreKey(dictType, locale) {
88
56
  return `${dictType}_${locale}`;
89
57
  }
90
- /** 从指定存储库读取字典缓存 */
58
+ /**
59
+ * 从指定仓库读取字典缓存。
60
+ *
61
+ * @param {string} storeName - 仓库名,如 'dicts'
62
+ * @param {string} dictType - 字典类型,如 'gender'
63
+ * @param {string} locale - 语言标识,如 'zh-CN'
64
+ * @returns {Promise<CacheEntry<DictEntry> | null>} 缓存条目,未命中返回 null
65
+ */
91
66
  async get(storeName, dictType, locale) {
92
- if (!this.db) return null;
93
- await this.ensureStore(storeName);
94
- if (!this.db) return null;
95
- return new Promise((resolve, reject) => {
96
- const tx = this.db.transaction(storeName, "readonly");
97
- const store = tx.objectStore(storeName);
98
- const request = store.get(this.getStoreKey(dictType, locale));
99
- request.addEventListener("success", () => resolve(request.result ?? null));
100
- request.addEventListener("error", () => reject(request.error));
101
- });
67
+ if (!this.db.isOpen()) return null;
68
+ if (!this.db.tables.some((t) => t.name === storeName)) return null;
69
+ const key = this.getStoreKey(dictType, locale);
70
+ const record = await this.db.table(storeName).get(key);
71
+ logger.debug(`IndexedDB get: store=${storeName} key=${key} hit=${!!record}`);
72
+ return record ?? null;
102
73
  }
103
- /** 写入字典缓存条目到指定存储库 */
74
+ /**
75
+ * 写入缓存条目到指定仓库。
76
+ *
77
+ * @param {string} storeName - 仓库名
78
+ * @param {string} dictType - 字典类型
79
+ * @param {string} locale - 语言标识
80
+ * @param {CacheEntry<DictEntry>} entry - 缓存条目
81
+ * @returns {Promise<void>}
82
+ */
104
83
  async set(storeName, dictType, locale, entry) {
105
- if (!this.db) return;
106
- await this.ensureStore(storeName);
107
- if (!this.db) return;
108
- return new Promise((resolve, reject) => {
109
- const tx = this.db.transaction(storeName, "readwrite");
110
- const store = tx.objectStore(storeName);
111
- const request = store.put(entry, this.getStoreKey(dictType, locale));
112
- request.addEventListener("success", () => resolve());
113
- request.addEventListener("error", () => reject(request.error));
114
- });
84
+ if (!this.db.isOpen()) return;
85
+ if (!this.db.tables.some((t) => t.name === storeName)) return;
86
+ const key = this.getStoreKey(dictType, locale);
87
+ logger.debug(`IndexedDB set: store=${storeName} key=${key}`);
88
+ await this.db.table(storeName).put(entry, key);
115
89
  }
116
90
  /**
117
- * 清空字典缓存数据。
118
- * @param storeName 指定要清空的存储库名,不传则清空全部存储库
91
+ * 清空缓存数据。
92
+ *
93
+ * @param {string} [storeName] - 指定仓库名则只清该仓库,不传则清空全部
94
+ * @returns {Promise<void>}
119
95
  */
120
96
  async clear(storeName) {
121
- if (!this.db) return;
97
+ if (!this.db.isOpen()) return;
122
98
  if (storeName) {
123
- if (!this.db.objectStoreNames.contains(storeName)) return;
124
- await new Promise((resolve, reject) => {
125
- const tx = this.db.transaction(storeName, "readwrite");
126
- const store = tx.objectStore(storeName);
127
- const request = store.clear();
128
- request.addEventListener("success", () => resolve());
129
- request.addEventListener("error", () => reject(request.error));
130
- });
131
- return;
132
- }
133
- const storeNames = Array.from(this.db.objectStoreNames);
134
- for (const name of storeNames) {
135
- await new Promise((resolve, reject) => {
136
- const tx = this.db.transaction(name, "readwrite");
137
- const store = tx.objectStore(name);
138
- const request = store.clear();
139
- request.addEventListener("success", () => resolve());
140
- request.addEventListener("error", () => reject(request.error));
141
- });
99
+ if (!this.db.tables.some((t) => t.name === storeName)) return;
100
+ logger.debug(`IndexedDB clear: store=${storeName}`);
101
+ await this.db.table(storeName).clear();
102
+ } else {
103
+ logger.debug("IndexedDB clear: ALL");
104
+ for (const table of this.db.tables) {
105
+ await table.clear();
106
+ }
142
107
  }
143
108
  }
144
109
  }
@@ -9,8 +9,7 @@ import type { DictAdapter } from '../types/index.js';
9
9
  * @returns {DictAdapter} 原样返回传入的适配器对象
10
10
  *
11
11
  * @example
12
- * // ~/dict/dict-adapter.ts
13
- * import { defineDictAdapter } from '@lacqjs/nuxt-dict'
12
+ * // ~/dict/dict-adapter.ts — defineDictAdapter 已自动导入,无需 import
14
13
  *
15
14
  * export default defineDictAdapter({
16
15
  * async fetchDict(storeName, { types, locale }) {
@@ -20,6 +20,8 @@ export interface DictManagerOptions {
20
20
  ttl: number;
21
21
  /** localStorage 中存储版本号的 key */
22
22
  versionStorageKey: string;
23
+ /** 配置为惰性版本检查的仓库集合,不在此集合中的仓库由 initialize() 立即检查 */
24
+ lazyStores: Set<string>;
23
25
  }
24
26
  /**
25
27
  * 字典管理器 —— 核心调度层。
@@ -46,6 +48,8 @@ export declare class DictManager {
46
48
  private checkedStores;
47
49
  /** 正在进行的版本检查请求,用于去重 */
48
50
  private pendingVersionChecks;
51
+ /** 配置为惰性版本检查的仓库集合 */
52
+ private lazyStores;
49
53
  locale: import("vue").ShallowRef<string, string>;
50
54
  constructor(options: DictManagerOptions);
51
55
  /** 构建带存储库命名空间和语言后缀的缓存键 */
@@ -66,7 +70,7 @@ export declare class DictManager {
66
70
  */
67
71
  getLocale(): string;
68
72
  /**
69
- * 惰性版本检查:首次访问该仓库时检查版本变更,按需清理缓存。
73
+ * 惰性版本检查(仅 lazy 仓库使用):首次访问该仓库时检查版本变更,按需清理缓存。
70
74
  * 并发调用去重 —— 同一仓库的多个 getDict 共享单次版本检查。
71
75
  */
72
76
  private ensureVersionChecked;
@@ -126,8 +130,8 @@ export declare class DictManager {
126
130
  */
127
131
  private codeMatch;
128
132
  /**
129
- * 初始化管理器。
130
- * 版本检查已改为惰性执行,在首次 getDict() 调用时触发,避免全量串行请求。
133
+ * 初始化管理器:对非 lazy 仓库立即并行检查版本号。
134
+ * lazy 仓库的版本检查延迟到首次 getDict() 调用时惰性触发。
131
135
  *
132
136
  * @returns {Promise<void>}
133
137
  */
@@ -2,6 +2,8 @@ import { shallowRef } from "vue";
2
2
  import { DEFAULT_STORE_NAME } from "./cache/indexeddb-cache.js";
3
3
  import { MemoryCache } from "./cache/memory-cache.js";
4
4
  import { VersionCheck } from "./cache/version-check.js";
5
+ import { createLogger } from "../utils/logger.js";
6
+ const logger = createLogger("nuxt-dict");
5
7
  export class DictManager {
6
8
  memoryCache;
7
9
  indexedDB;
@@ -14,12 +16,15 @@ export class DictManager {
14
16
  checkedStores = /* @__PURE__ */ new Set();
15
17
  /** 正在进行的版本检查请求,用于去重 */
16
18
  pendingVersionChecks = /* @__PURE__ */ new Map();
19
+ /** 配置为惰性版本检查的仓库集合 */
20
+ lazyStores;
17
21
  locale = shallowRef("zh-CN");
18
22
  constructor(options) {
19
23
  this.memoryCache = new MemoryCache(options.memoryMax, options.ttl);
20
24
  this.indexedDB = options.indexedDB;
21
25
  this.adapters = options.adapters;
22
26
  this.pendingRequests = /* @__PURE__ */ new Map();
27
+ this.lazyStores = options.lazyStores;
23
28
  this.versionChecks = /* @__PURE__ */ new Map();
24
29
  for (const [storeName, adapter] of options.adapters) {
25
30
  const storageKey = storeName === DEFAULT_STORE_NAME ? options.versionStorageKey : `${options.versionStorageKey}__${storeName}`;
@@ -56,7 +61,7 @@ export class DictManager {
56
61
  return this.locale.value;
57
62
  }
58
63
  /**
59
- * 惰性版本检查:首次访问该仓库时检查版本变更,按需清理缓存。
64
+ * 惰性版本检查(仅 lazy 仓库使用):首次访问该仓库时检查版本变更,按需清理缓存。
60
65
  * 并发调用去重 —— 同一仓库的多个 getDict 共享单次版本检查。
61
66
  */
62
67
  async ensureVersionChecked(storeName) {
@@ -79,9 +84,9 @@ export class DictManager {
79
84
  }
80
85
  })() : Promise.resolve();
81
86
  this.pendingVersionChecks.set(storeName, promise);
82
- this.checkedStores.add(storeName);
83
87
  try {
84
88
  await promise;
89
+ this.checkedStores.add(storeName);
85
90
  } finally {
86
91
  this.pendingVersionChecks.delete(storeName);
87
92
  }
@@ -95,7 +100,10 @@ export class DictManager {
95
100
  * @returns {Promise<DictEntry>} 包含 items 和可选 tree 的字典条目
96
101
  */
97
102
  async getDict(type, storeName = DEFAULT_STORE_NAME) {
98
- await this.ensureVersionChecked(storeName);
103
+ logger.debug(`getDict START store=${storeName} type=${type} lazy=${this.lazyStores.has(storeName)}`);
104
+ if (this.lazyStores.has(storeName)) {
105
+ await this.ensureVersionChecked(storeName);
106
+ }
99
107
  const key = this.buildKey(type, storeName);
100
108
  const memoryEntry = this.memoryCache.get(key);
101
109
  if (memoryEntry) {
@@ -115,7 +123,9 @@ export class DictManager {
115
123
  }
116
124
  /** 执行实际的数据获取与缓存写入 */
117
125
  async fetchAndCache(type, storeName, key) {
126
+ logger.debug(`fetchAndCache START store=${storeName} type=${type}`);
118
127
  const idbEntry = await this.indexedDB.get(storeName, type, this.locale.value);
128
+ logger.debug(`fetchAndCache IndexedDB result store=${storeName} type=${type} hit=${!!idbEntry}`);
119
129
  if (idbEntry) {
120
130
  this.memoryCache.set(key, {
121
131
  data: idbEntry.data,
@@ -124,12 +134,14 @@ export class DictManager {
124
134
  });
125
135
  return idbEntry.data;
126
136
  }
137
+ logger.debug(`fetchAndCache NETWORK requesting store=${storeName} type=${type}`);
127
138
  const adapter = this.getAdapter(storeName);
128
139
  const response = await adapter.fetchDict(storeName, {
129
140
  types: [type],
130
141
  locale: this.locale.value
131
142
  });
132
143
  const entry = response.data[type];
144
+ logger.debug(`fetchAndCache NETWORK done store=${storeName} type=${type} found=${!!entry} items=${entry?.items?.length}`);
133
145
  if (!entry) {
134
146
  throw new Error(`Dictionary type "${type}" not found in response`);
135
147
  }
@@ -253,12 +265,24 @@ export class DictManager {
253
265
  return String(a) === String(b);
254
266
  }
255
267
  /**
256
- * 初始化管理器。
257
- * 版本检查已改为惰性执行,在首次 getDict() 调用时触发,避免全量串行请求。
268
+ * 初始化管理器:对非 lazy 仓库立即并行检查版本号。
269
+ * lazy 仓库的版本检查延迟到首次 getDict() 调用时惰性触发。
258
270
  *
259
271
  * @returns {Promise<void>}
260
272
  */
261
273
  async initialize() {
274
+ if (typeof localStorage === "undefined") return;
275
+ const immediateStores = [...this.versionChecks].filter(([name]) => !this.lazyStores.has(name));
276
+ await Promise.all(immediateStores.map(async ([name, vc]) => {
277
+ try {
278
+ const { changed } = await vc.check(name);
279
+ if (changed) {
280
+ await this.invalidateAll(name);
281
+ }
282
+ } catch {
283
+ }
284
+ this.checkedStores.add(name);
285
+ }));
262
286
  }
263
287
  /**
264
288
  * 失效缓存数据。
@@ -4,7 +4,8 @@ export const defaultOptions = {
4
4
  api: {
5
5
  baseURL: "/api",
6
6
  dictEndpoint: "/dict/list",
7
- versionEndpoint: "/dict/version"
7
+ versionEndpoint: "/dict/version",
8
+ lazy: false
8
9
  },
9
10
  cache: {
10
11
  memoryMax: 200,
@@ -54,13 +54,26 @@ function resolveClientLocale(options) {
54
54
  }
55
55
  return options.locale.default;
56
56
  }
57
- async function initClientCache(manager, indexedDB, logger) {
58
- try {
59
- await indexedDB.init();
60
- await manager.initialize();
61
- } catch (e) {
62
- logger.warn("IndexedDB init failed:", e);
57
+ async function initClient(manager, indexedDB, options, logger) {
58
+ if (options.cache.indexedDB.enabled) {
59
+ try {
60
+ const storeNames = [DEFAULT_STORE_NAME, ...Object.keys(options.stores)];
61
+ await indexedDB.init(storeNames);
62
+ } catch (e) {
63
+ logger.warn("IndexedDB init failed:", e);
64
+ }
63
65
  }
66
+ await manager.initialize();
67
+ }
68
+ function resolveLazyStores(options) {
69
+ const lazyStores = /* @__PURE__ */ new Set();
70
+ if (options.api.lazy) lazyStores.add(DEFAULT_STORE_NAME);
71
+ for (const [name, config] of Object.entries(options.stores)) {
72
+ if (config.lazy ?? options.api.lazy ?? false) {
73
+ lazyStores.add(name);
74
+ }
75
+ }
76
+ return lazyStores;
64
77
  }
65
78
  async function executePrefetch(manager, types, logger) {
66
79
  try {
@@ -102,12 +115,14 @@ const dictPlugin = defineNuxtPlugin(async (nuxtApp) => {
102
115
  const logger = createLogger("nuxt-dict", { level: options.logLevel });
103
116
  const adapters = createAdapters(options, logger);
104
117
  const indexedDB = new IndexedDBCache(options.cache.indexedDB.dbName);
118
+ const lazyStores = resolveLazyStores(options);
105
119
  const manager = new DictManager({
106
120
  adapters,
107
121
  indexedDB,
108
122
  memoryMax: options.cache.memoryMax,
109
123
  ttl: options.cache.ttl,
110
- versionStorageKey: options.version.storageKey
124
+ versionStorageKey: options.version.storageKey,
125
+ lazyStores
111
126
  });
112
127
  const locale = import.meta.server ? resolveServerLocale(options) : resolveClientLocale(options);
113
128
  manager.setLocale(locale);
@@ -120,8 +135,8 @@ const dictPlugin = defineNuxtPlugin(async (nuxtApp) => {
120
135
  }
121
136
  });
122
137
  }
123
- if (import.meta.client && options.cache.indexedDB.enabled) {
124
- await initClientCache(manager, indexedDB, logger);
138
+ if (import.meta.client) {
139
+ await initClient(manager, indexedDB, options, logger);
125
140
  }
126
141
  if (import.meta.server && options.ssr.prefetch.length > 0) {
127
142
  await executePrefetch(manager, options.ssr.prefetch, logger);
@@ -88,6 +88,8 @@ export interface StoreApiOptions {
88
88
  versionEndpoint?: string;
89
89
  /** 自定义字典适配器文件路径(如 '~/dict/dict-adapter'),不传则按约定路径自动发现或使用默认 REST 适配器 */
90
90
  adapter?: string;
91
+ /** 是否惰性检查版本号,默认继承全局 `api.lazy`。`true` 时首次 getDict 调用才检查,`false` 时页面加载立即检查 */
92
+ lazy?: boolean;
91
93
  }
92
94
  /** 模块配置项(用户可传,字段均可选) */
93
95
  export interface ModuleOptions {
@@ -104,6 +106,8 @@ export interface ModuleOptions {
104
106
  versionEndpoint?: string;
105
107
  /** 自定义字典适配器文件路径(如 '~/dict/dict-adapter'),不传则按约定路径自动发现或使用默认 REST 适配器 */
106
108
  adapter?: string;
109
+ /** 是否惰性检查版本号,默认 `false`(页面加载时立即检查)。`true` 时首次 getDict 调用才检查 */
110
+ lazy?: boolean;
107
111
  };
108
112
  cache?: {
109
113
  /** 内存缓存最大条目数,默认 `200` */
@@ -156,6 +160,7 @@ export interface ResolvedModuleOptions {
156
160
  dictEndpoint: string;
157
161
  versionEndpoint: string;
158
162
  adapter?: string;
163
+ lazy: boolean;
159
164
  };
160
165
  cache: {
161
166
  memoryMax: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lacqjs/nuxt-dict",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "description": "Nuxt 数据字典模块,提供扁平/树形字典翻译、多语言国际化、三级缓存与 SSR 预取。",
5
5
  "keywords": [
6
6
  "@lacqjs/nuxt-dict",
@@ -44,7 +44,8 @@
44
44
  "@nuxt/kit": "^4.4.8",
45
45
  "compare-versions": "^6.1.1",
46
46
  "consola": "^3.4.2",
47
- "defu": "^6.1.7"
47
+ "defu": "^6.1.7",
48
+ "dexie": "^4.4.4"
48
49
  },
49
50
  "devDependencies": {
50
51
  "@nuxt/devtools": "^3.2.4",