@@ -33,7 +33,7 @@ import type {
3333 ScriptMenuItemOption ,
3434} from "@App/app/service/service_worker/types" ;
3535import { popupClient , runtimeClient , scriptClient } from "@App/pages/store/features/script" ;
36- import { i18nLang , i18nName } from "@App/locales/locales" ;
36+ import { i18nName } from "@App/locales/locales" ;
3737
3838// 用于读取 metadata
3939const scriptDAO = new ScriptDAO ( ) ;
@@ -329,8 +329,6 @@ type ScriptMenuEntry = ScriptMenu & {
329329 metadata : SCMetadata ;
330330} ;
331331
332- let scriptDataAsyncCounter = 0 ;
333-
334332// Popup 页面使用的脚本/选单清单元件:只负责渲染与互动,状态与持久化交由外部 client 处理。
335333const ScriptMenuList = React . memo (
336334 ( {
@@ -346,15 +344,6 @@ const ScriptMenuList = React.memo(
346344 currentUrl : string ;
347345 menuExpandNum : number ;
348346 } ) => {
349- // extraData 为 undefined 时先等待异步加载完成,避免重复渲染
350- const [ extraData , setExtraData ] = useState <
351- | {
352- uuids : string ;
353- lang : string ;
354- metadata : Record < string , SCMetadata > ;
355- }
356- | undefined
357- > ( undefined ) ;
358347 const [ scriptMenuList , setScriptMenuList ] = useState < ScriptMenuEntry [ ] > ( [ ] ) ;
359348 const { t } = useTranslation ( ) ;
360349
@@ -417,48 +406,34 @@ const ScriptMenuList = React.memo(
417406 return url ;
418407 } , [ currentUrl ] ) ;
419408
420- // string memo 避免 uuids 以外的改变影响
421- const uuids = useMemo ( ( ) => script . map ( ( item ) => item . uuid ) . join ( "\n" ) , [ script ] ) ;
422- // eslint-disable-next-line react-hooks/exhaustive-deps
423- const lang = useMemo ( ( ) => i18nLang ( ) , [ t ] ) ; // 当 t 改变时,重新检查当前页面语言
424-
409+ const cache = useMemo ( ( ) => new Map < string , SCMetadata | undefined > ( ) , [ ] ) ;
425410 // 以 异步方式 取得 metadata 放入 extraData
426411 // script 或 extraData 的更新时都会再次执行
427412 useEffect ( ( ) => {
428- if ( extraData && extraData . uuids === uuids && extraData . lang === lang ) {
429- // extraData 已取得
430- // 把 getPopupData() 的 scriptMenuList 和 异步结果 的 metadata 合并至 scriptMenuList
431- const metadata = extraData . metadata ;
432- const newScriptMenuList = script . map ( ( item ) => ( { ...item , metadata : metadata [ item . uuid ] || { } } ) ) ;
433- updateScriptMenuList ( newScriptMenuList ) ;
434- } else {
435- // 取得 extraData
436- scriptDataAsyncCounter = ( scriptDataAsyncCounter % 255 ) + 1 ; // 轮出 1 ~ 255
437- const lastCounter = scriptDataAsyncCounter ;
438- scriptDAO . gets ( uuids . split ( "\n" ) ) . then ( ( res ) => {
439- if ( lastCounter !== scriptDataAsyncCounter ) {
440- // 由于 state 改变,在结果取得前 useEffect 再次执行,因此需要忽略上次结果
441- return ;
442- }
443- const metadataRecord = { } as Record < string , SCMetadata > ;
444- const nameKey = `name:${ lang } ` ;
445- for ( const entry of res ) {
446- if ( entry ) {
447- const m = entry . metadata ;
448- const [ icon ] = m . icon || m . iconurl || m . icon64 || m . icon64url || [ ] ;
449- // metadataRecord 的储存量不影响 storage.session 但影响页面的记忆体
450- // 按需要可以增加其他 metadata, 例如 @match @include @exclude
451- metadataRecord [ entry . uuid ] = {
452- icon : [ icon ] , // 只储存单个 icon
453- [ nameKey ] : [ i18nName ( entry ) ] , // 只储存 i18n 的 name
454- } satisfies SCMetadata ;
413+ let isMounted = true ;
414+ // 先从 cache 读取,避免重复请求相同 uuid 的 metadata
415+ Promise . all (
416+ script . map ( async ( item ) => {
417+ let metadata = cache . get ( item . uuid ) ;
418+ if ( ! metadata ) {
419+ const script = await scriptDAO . get ( item . uuid ) ;
420+ if ( script ) {
421+ metadata = script . metadata || { } ;
455422 }
423+ cache . set ( item . uuid , metadata ) ;
456424 }
457- setExtraData ( { uuids, lang, metadata : metadataRecord } ) ;
458- // 再次触发 useEffect
459- } ) ;
460- }
461- } , [ script , uuids , lang , extraData ] ) ;
425+ return { ...item , metadata : metadata || { } } ;
426+ } )
427+ ) . then ( ( newScriptMenuList ) => {
428+ if ( ! isMounted ) {
429+ return ;
430+ }
431+ updateScriptMenuList ( newScriptMenuList ) ;
432+ } ) ;
433+ return ( ) => {
434+ isMounted = false ;
435+ } ;
436+ } , [ cache , script ] ) ;
462437
463438 useEffect ( ( ) => {
464439 // 注册菜单快速键(accessKey):以各分组第一个项目的 accessKey 作为触发条件。
0 commit comments