-
Notifications
You must be signed in to change notification settings - Fork 298
[v1.3] 把 metadata 从 chrome.storage.session 抽走 #1027
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: release/v1.3
Are you sure you want to change the base?
[v1.3] 把 metadata 从 chrome.storage.session 抽走 #1027
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
此 PR 通过将脚本元数据(metadata)从 chrome.storage.session 迁移到按需从 IndexedDB 异步加载的方式,解决了 storage.session 存储空间不足的问题(issue #1026)。
主要变更:
- 在 Popup 页面组件中实现异步加载 metadata,仅提取必要的图标和国际化名称数据
- 从
ScriptMenu类型中移除 metadata 字段,减少 session storage 占用 - 提取
i18nLang()辅助函数以支持语言变化检测
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
src/pages/components/ScriptMenuList/index.tsx |
主要改动:新增异步加载 metadata 的逻辑,通过 ScriptDAO 从 IndexedDB 读取并合并到脚本列表中 |
src/locales/locales.ts |
提取 i18nLang() 辅助函数,用于获取当前语言标识 |
src/app/service/service_worker/types.ts |
从 ScriptMenu 类型定义中移除 metadata 字段,添加说明注释 |
src/app/service/service_worker/popup_scriptmenu.ts |
注释掉 metadata 赋值逻辑,与类型定义保持一致 |
src/app/service/service_worker/popup.ts |
注释掉不再需要的 getCombinedMeta 调用 |
| } | ||
| } | ||
| setExtraData({ uuids, lang, metadata: metadataRecord }); | ||
| // 再次触发 useEffect |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Promise 缺少错误处理。如果 scriptDAO.gets() 失败(例如 IndexedDB 访问错误),将会导致未捕获的 Promise 拒绝。建议添加 .catch() 处理错误情况,例如:
scriptDAO.gets(uuids.split("\n")).then((res) => {
// ... 现有逻辑
}).catch((error) => {
console.error("Failed to load script metadata:", error);
// 降级处理:使用空的 metadata
setExtraData({ uuids, lang, metadata: {} });
});| // 再次触发 useEffect | |
| // 再次触发 useEffect | |
| }).catch((error) => { | |
| console.error("Failed to load script metadata:", error); | |
| // 降级处理:使用空的 metadata | |
| setExtraData({ uuids, lang, metadata: {} }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
雖然不預期出錯
| if (extraData && extraData.uuids === uuids && extraData.lang === lang) { | ||
| // extraData 已取得 | ||
| // 把 getPopupData() 的 scriptMenuList 和 异步结果 的 metadata 合并至 scriptMenuList | ||
| const metadata = extraData.metadata; | ||
| const newScriptMenuList = script.map((item) => ({ ...item, metadata: metadata[item.uuid] || {} })); | ||
| updateScriptMenuList(newScriptMenuList); | ||
| } else { | ||
| // 取得 extraData | ||
| scriptDataAsyncCounter = (scriptDataAsyncCounter % 255) + 1; // 轮出 1 ~ 255 | ||
| const lastCounter = scriptDataAsyncCounter; | ||
| scriptDAO.gets(uuids.split("\n")).then((res) => { | ||
| if (lastCounter !== scriptDataAsyncCounter) { | ||
| // 由于 state 改变,在结果取得前 useEffect 再次执行,因此需要忽略上次结果 | ||
| return; | ||
| } | ||
| const metadataRecord = {} as Record<string, SCMetadata>; | ||
| const nameKey = `name:${lang}`; | ||
| for (const entry of res) { | ||
| if (entry) { | ||
| const m = entry?.metadata; | ||
| const [icon] = m.icon || m.iconurl || m.icon64 || m.icon64url || []; | ||
| // metadataRecord 的储存量不影响 storage.session 但影响页面的记忆体 | ||
| // 按需要可以增加其他 metadata, 例如 @match @include @exclude | ||
| metadataRecord[entry.uuid] = { | ||
| icon: [icon], // 只储存单个 icon | ||
| [nameKey]: [i18nName(entry)], // 只储存 i18n 的 name | ||
| } satisfies SCMetadata; | ||
| } | ||
| } | ||
| setExtraData({ uuids, lang, metadata: metadataRecord }); | ||
| // 再次触发 useEffect | ||
| }); | ||
| } | ||
| }, [script, uuids, lang, extraData]); |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
useEffect 的依赖数组包含 extraData 会导致额外的 effect 执行。每次 setExtraData 调用都会触发 effect 重新运行,即使第 428-433 行的逻辑可以正确处理,这仍然是不必要的性能开销。
建议优化:将第 432-433 行的逻辑移到 Promise 的 then 回调中,直接更新 scriptMenuList,而不是通过 setExtraData 触发另一次 effect:
scriptDAO.gets(uuids.split("\n")).then((res) => {
if (lastCounter !== scriptDataAsyncCounter) return;
const metadataRecord = {} as Record<string, SCMetadata>;
// ... 构建 metadataRecord 的逻辑 ...
const newScriptMenuList = script.map((item) => ({
...item,
metadata: metadataRecord[item.uuid] || {}
}));
updateScriptMenuList(newScriptMenuList);
setExtraData({ uuids, lang, metadata: metadataRecord });
});然后从依赖数组中移除 extraData。
| if (extraData && extraData.uuids === uuids && extraData.lang === lang) { | |
| // extraData 已取得 | |
| // 把 getPopupData() 的 scriptMenuList 和 异步结果 的 metadata 合并至 scriptMenuList | |
| const metadata = extraData.metadata; | |
| const newScriptMenuList = script.map((item) => ({ ...item, metadata: metadata[item.uuid] || {} })); | |
| updateScriptMenuList(newScriptMenuList); | |
| } else { | |
| // 取得 extraData | |
| scriptDataAsyncCounter = (scriptDataAsyncCounter % 255) + 1; // 轮出 1 ~ 255 | |
| const lastCounter = scriptDataAsyncCounter; | |
| scriptDAO.gets(uuids.split("\n")).then((res) => { | |
| if (lastCounter !== scriptDataAsyncCounter) { | |
| // 由于 state 改变,在结果取得前 useEffect 再次执行,因此需要忽略上次结果 | |
| return; | |
| } | |
| const metadataRecord = {} as Record<string, SCMetadata>; | |
| const nameKey = `name:${lang}`; | |
| for (const entry of res) { | |
| if (entry) { | |
| const m = entry?.metadata; | |
| const [icon] = m.icon || m.iconurl || m.icon64 || m.icon64url || []; | |
| // metadataRecord 的储存量不影响 storage.session 但影响页面的记忆体 | |
| // 按需要可以增加其他 metadata, 例如 @match @include @exclude | |
| metadataRecord[entry.uuid] = { | |
| icon: [icon], // 只储存单个 icon | |
| [nameKey]: [i18nName(entry)], // 只储存 i18n 的 name | |
| } satisfies SCMetadata; | |
| } | |
| } | |
| setExtraData({ uuids, lang, metadata: metadataRecord }); | |
| // 再次触发 useEffect | |
| }); | |
| } | |
| }, [script, uuids, lang, extraData]); | |
| // 取得 extraData | |
| scriptDataAsyncCounter = (scriptDataAsyncCounter % 255) + 1; // 轮出 1 ~ 255 | |
| const lastCounter = scriptDataAsyncCounter; | |
| scriptDAO.gets(uuids.split("\n")).then((res) => { | |
| if (lastCounter !== scriptDataAsyncCounter) { | |
| // 由于 state 改变,在结果取得前 useEffect 再次执行,因此需要忽略上次结果 | |
| return; | |
| } | |
| const metadataRecord = {} as Record<string, SCMetadata>; | |
| const nameKey = `name:${lang}`; | |
| for (const entry of res) { | |
| if (entry) { | |
| const m = entry?.metadata; | |
| const [icon] = m.icon || m.iconurl || m.icon64 || m.icon64url || []; | |
| // metadataRecord 的储存量不影响 storage.session 但影响页面的记忆体 | |
| // 按需要可以增加其他 metadata, 例如 @match @include @exclude | |
| metadataRecord[entry.uuid] = { | |
| icon: [icon], // 只储存单个 icon | |
| [nameKey]: [i18nName(entry)], // 只储存 i18n 的 name | |
| } satisfies SCMetadata; | |
| } | |
| } | |
| const newScriptMenuList = script.map((item) => ({ ...item, metadata: metadataRecord[item.uuid] || {} })); | |
| updateScriptMenuList(newScriptMenuList); | |
| }); | |
| }, [script, uuids, lang]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ExtraData不是只用來單次儲取
下次也能用
否則每次scripts參考改變就會fetch一次
相反useEffect時裡面沒有複雜的指令,不會有效能問題
| } | ||
| | undefined | ||
| >(undefined); | ||
| const [scriptMenuList, setScriptMenuList] = useState<ScriptMenuEntry[]>([]); |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
初始状态下 scriptMenuList 为空数组,会导致在异步加载 metadata 期间显示"无数据",即使实际上有脚本存在。这会造成不好的用户体验(闪烁)。
建议的解决方案:
- 初始状态设为
undefined来区分"加载中"和"无数据":
const [scriptMenuList, setScriptMenuList] = useState<ScriptMenuEntry[] | undefined>(undefined);- 在渲染时增加加载状态判断:
{scriptMenuList === undefined ? (
<Spin /> // 或其他加载指示器
) : scriptMenuList.length === 0 ? (
<Empty description={t("no_data")} />
) : (
scriptMenuList.map(...)
)}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
有道理
可以研究一下
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
概述 Descriptions
CACHE_KEY_TAB_SCRIPT(popup 用的)tabScript 不要储存 metadata #1026close #1026
变更内容 Changes
截图 Screenshots