Skip to content

Conversation

@cyfung1031
Copy link
Collaborator

@cyfung1031 cyfung1031 commented Nov 27, 2025

概述 Descriptions

close #1026

变更内容 Changes

截图 Screenshots

@cyfung1031 cyfung1031 changed the title 把 metadata 从 chrome.storage.session 抽取 把 metadata 从 chrome.storage.session 抽走 Nov 27, 2025
@CodFrm CodFrm requested a review from Copilot November 28, 2025 03:59
Copilot finished reviewing on behalf of CodFrm November 28, 2025 04:02
Copy link
Contributor

Copilot AI left a 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
Copy link

Copilot AI Nov 28, 2025

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: {} });
});
Suggested change
// 再次触发 useEffect
// 再次触发 useEffect
}).catch((error) => {
console.error("Failed to load script metadata:", error);
// 降级处理:使用空的 metadata
setExtraData({ uuids, lang, metadata: {} });

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

雖然不預期出錯

Comment on lines 428 to 461
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]);
Copy link

Copilot AI Nov 28, 2025

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

Suggested change
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]);

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

@cyfung1031 cyfung1031 Nov 28, 2025

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[]>([]);
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

初始状态下 scriptMenuList 为空数组,会导致在异步加载 metadata 期间显示"无数据",即使实际上有脚本存在。这会造成不好的用户体验(闪烁)。

建议的解决方案:

  1. 初始状态设为 undefined 来区分"加载中"和"无数据":
const [scriptMenuList, setScriptMenuList] = useState<ScriptMenuEntry[] | undefined>(undefined);
  1. 在渲染时增加加载状态判断:
{scriptMenuList === undefined ? (
  <Spin /> // 或其他加载指示器
) : scriptMenuList.length === 0 ? (
  <Empty description={t("no_data")} />
) : (
  scriptMenuList.map(...)
)}

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

有道理
可以研究一下

@cyfung1031 cyfung1031 changed the title 把 metadata 从 chrome.storage.session 抽走 [v1.3] 把 metadata 从 chrome.storage.session 抽走 Dec 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant