This is an automated email from the ASF dual-hosted git repository. robin0716 pushed a commit to branch dev in repository https://gitbox.apache.org/repos/asf/incubator-answer.git
commit 8753716ceb789d59effc5c3613d0215775c225ec Author: robin <[email protected]> AuthorDate: Mon Oct 21 16:19:32 2024 +0800 refactor(ui): optimize initial loading and extract event logic of plugin kit out --- ui/src/App.tsx | 33 ++++-------------------------- ui/src/router/index.tsx | 22 +++++++++++++------- ui/src/utils/pluginKit/emitter.ts | 34 ------------------------------- ui/src/utils/pluginKit/index.ts | 42 +++++++++++---------------------------- 4 files changed, 31 insertions(+), 100 deletions(-) diff --git a/ui/src/App.tsx b/ui/src/App.tsx index f799042a..8c425f29 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -17,44 +17,19 @@ * under the License. */ -import { - type RouteObject, - RouterProvider, - createBrowserRouter, -} from 'react-router-dom'; -import { useState, useEffect } from 'react'; +import { RouterProvider, createBrowserRouter } from 'react-router-dom'; import './i18n/init'; -import { subscribe, unsubscribe } from '@/utils/pluginKit'; -import resolveRoutes from '@/router'; +import '@/utils/pluginKit'; +import { useMergeRoutes } from '@/router'; import InitialLoadingPlaceholder from '@/components/InitialLoadingPlaceholder'; -function useResolvedRoutes() { - const [routes, setRoutes] = useState<RouteObject[]>([]); - - useEffect(() => { - const callback = () => { - setRoutes(resolveRoutes()); - }; - - subscribe('registered', callback); - - return () => { - unsubscribe('registered', callback); - }; - }, []); - - return routes; -} - function App() { - const routes = useResolvedRoutes(); - + const routes = useMergeRoutes(); if (routes.length === 0) { return <InitialLoadingPlaceholder />; } - const router = createBrowserRouter(routes, { basename: process.env.REACT_APP_BASE_URL, }); diff --git a/ui/src/router/index.tsx b/ui/src/router/index.tsx index 8c111e0f..ea2fdf3c 100644 --- a/ui/src/router/index.tsx +++ b/ui/src/router/index.tsx @@ -17,7 +17,7 @@ * under the License. */ -import { Suspense, lazy } from 'react'; +import { Suspense, lazy, useEffect, useState } from 'react'; import { RouteObject } from 'react-router-dom'; import Layout from '@/pages/Layout'; @@ -75,13 +75,21 @@ const routeWrapper = (routeNodes: RouteNode[], root: RouteNode[]) => { }); }; -function resolveRoutes(): RouteObject[] { - const routes: RouteNode[] = []; - const mergedRoutes = mergeRoutePlugins(baseRoutes); +function useMergeRoutes() { + const [routesState, setRoutes] = useState<RouteObject[]>([]); - routeWrapper(mergedRoutes, routes); + const init = async () => { + const routes = []; + const mergedRoutes = await mergeRoutePlugins(baseRoutes).catch(() => []); + routeWrapper(mergedRoutes, routes); + setRoutes(routes); + }; - return routes as RouteObject[]; + useEffect(() => { + init(); + }, []); + + return routesState; } -export default resolveRoutes; +export { useMergeRoutes }; diff --git a/ui/src/utils/pluginKit/emitter.ts b/ui/src/utils/pluginKit/emitter.ts deleted file mode 100644 index eee427b6..00000000 --- a/ui/src/utils/pluginKit/emitter.ts +++ /dev/null @@ -1,34 +0,0 @@ -type EventName = string; -type EventHandler = () => void; - -class SimpleEventEmitter { - events: Record<EventName, EventHandler[]> = {}; - - on(name: EventName, handler: EventHandler) { - if (!this.events[name]) { - this.events[name] = []; - } - - this.events[name].push(handler); - } - - off(name: EventName, handler?: EventHandler) { - const handlers = this.events[name]; - - if (!handlers || handlers.length === 0) { - return; - } - - if (handler) { - this.events[name] = handlers.filter((func) => func !== handler); - } else { - delete this.events[name]; - } - } - - emit(name: EventName) { - (this.events[name] || []).forEach((handler) => handler()); - } -} - -export default SimpleEventEmitter; diff --git a/ui/src/utils/pluginKit/index.ts b/ui/src/utils/pluginKit/index.ts index 39d1edb5..346c1456 100644 --- a/ui/src/utils/pluginKit/index.ts +++ b/ui/src/utils/pluginKit/index.ts @@ -29,7 +29,6 @@ import request from '@/utils/request'; import { initI18nResource } from './utils'; import { Plugin, PluginInfo, PluginType } from './interface'; -import SimpleEventEmitter from './emitter'; /** * This information is to be defined for all components. @@ -43,23 +42,23 @@ import SimpleEventEmitter from './emitter'; * @field description: Plugin description, optionally configurable. Usually read from the `i18n` file */ -class Plugins extends SimpleEventEmitter { +class Plugins { plugins: Plugin[] = []; registeredPlugins: Type.ActivatedPlugin[] = []; + initialization: Promise<void>; + constructor() { - super(); - this.init(); + this.initialization = this.init(); } - init() { + async init() { this.registerBuiltin(); - getPluginsStatus().then((plugins) => { - this.registeredPlugins = plugins.filter((p) => p.enabled); - this.registerPlugins(); - }); + const plugins = await getPluginsStatus().catch(() => []); + this.registeredPlugins = plugins.filter((p) => p.enabled); + await this.registerPlugins(); } refresh() { @@ -105,7 +104,6 @@ class Plugins extends SimpleEventEmitter { .filter((p) => p); return Promise.all(plugins.map((p) => p())).then((resolvedPlugins) => { resolvedPlugins.forEach((plugin) => this.register(plugin)); - this.emit('registered'); return true; }); } @@ -122,18 +120,6 @@ class Plugins extends SimpleEventEmitter { this.plugins.push(plugin); } - activatePlugins(activatedPlugins: Type.ActivatedPlugin[]) { - this.plugins.forEach((plugin: any) => { - const { slug_name } = plugin.info; - const activatedPlugin: any = activatedPlugins?.find( - (p) => p.slug_name === slug_name, - ); - if (activatedPlugin) { - plugin.activated = activatedPlugin?.enabled; - } - }); - } - getPlugin(slug_name: string) { return this.plugins.find((p) => p.info.slug_name === slug_name); } @@ -150,10 +136,8 @@ class Plugins extends SimpleEventEmitter { const plugins = new Plugins(); -const subscribe = plugins.on.bind(plugins); -const unsubscribe = plugins.off.bind(plugins); - -const getRoutePlugins = () => { +const getRoutePlugins = async () => { + await plugins.initialization; return plugins .getPlugins() .filter((plugin) => plugin.info.type === PluginType.Route); @@ -183,8 +167,8 @@ const validateRoutePlugin = async (slugName) => { return Boolean(registeredPlugin?.enabled); }; -const mergeRoutePlugins = (routes) => { - const routePlugins = getRoutePlugins(); +const mergeRoutePlugins = async (routes) => { + const routePlugins = await getRoutePlugins(); if (routePlugins.length === 0) { return routes; } @@ -290,7 +274,5 @@ export { useCaptchaPlugin, useRenderPlugin, PluginType, - subscribe, - unsubscribe, }; export default plugins;
