mirror of https://github.com/alibaba/ice.git
				
				
				
			refactor: getAppData (#183)
* feat: data loader * refactor: put load logic to runtime * feat: pass matched ids * refactor: reuse initial context * feat: generate loader template * fix: name with slash * fix: name with slash * feat: throw error * feat: preload app data * refactor: initail context to request context * chore: add comment * chore: add comment * fix: type * fix: merge conflict * refactor: get app data * fix: conflict * fix: csr app data Co-authored-by: luhc228 <luhengchang228@126.com>
This commit is contained in:
		
							parent
							
								
									5af5199972
								
							
						
					
					
						commit
						27599db870
					
				|  | @ -1,4 +1,4 @@ | |||
| import { defineAppConfig } from 'ice'; | ||||
| import { GetAppData, GetAppConfig } from 'ice'; | ||||
| 
 | ||||
| if (process.env.ICE_CORE_ERROR_BOUNDARY === 'true') { | ||||
|   console.error('__REMOVED__'); | ||||
|  | @ -9,16 +9,21 @@ console.warn('__WARN__'); | |||
| console.error('__ERROR__'); | ||||
| console.log('process.env.HAHA', process.env.HAHA); | ||||
| 
 | ||||
| export default defineAppConfig({ | ||||
|   app: { | ||||
|     // @ts-expect-error loss tslib dependency
 | ||||
|     getData: async () => { | ||||
|       return { | ||||
|         title: 'gogogo', | ||||
|         auth: { | ||||
|           admin: true, | ||||
|         }, | ||||
|       }; | ||||
| export const getAppData: GetAppData = () => { | ||||
|   return new Promise((resolve) => { | ||||
|     resolve({ | ||||
|       title: 'gogogogo', | ||||
|       auth: { | ||||
|         admin: true, | ||||
|       }, | ||||
|     }); | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| export const getAppConfig: GetAppConfig = (appData) => { | ||||
|   return { | ||||
|     auth: { | ||||
|       initialAuth: appData?.auth, | ||||
|     }, | ||||
|   }, | ||||
| }); | ||||
|   }; | ||||
| }; | ||||
|  |  | |||
|  | @ -6,7 +6,6 @@ export default () => { | |||
| 
 | ||||
|   console.log('render Layout', 'data', data, 'config', config); | ||||
| 
 | ||||
| 
 | ||||
|   return ( | ||||
|     <div> | ||||
|       <h1>Layout</h1> | ||||
|  |  | |||
|  | @ -1,23 +1,24 @@ | |||
| import { defineAppConfig } from 'ice'; | ||||
| 
 | ||||
| if (process.env.ICE_CORE_ERROR_BOUNDARY) { | ||||
|   console.error('__REMOVED__'); | ||||
| } | ||||
| import { GetAppData, GetAppConfig } from 'ice'; | ||||
| 
 | ||||
| console.log('__LOG__'); | ||||
| console.warn('__WARN__'); | ||||
| console.error('__ERROR__'); | ||||
| 
 | ||||
| export default defineAppConfig({ | ||||
|   app: { | ||||
|     // @ts-expect-error loss tslib dependency
 | ||||
|     getData: async () => { | ||||
|       return { | ||||
|         title: 'gogogo', | ||||
|         auth: { | ||||
|           admin: true, | ||||
|         }, | ||||
|       }; | ||||
| export const getAppData: GetAppData = () => { | ||||
|   return new Promise((resolve) => { | ||||
|     resolve({ | ||||
|       title: 'gogogogo', | ||||
|       auth: { | ||||
|         admin: true, | ||||
|       }, | ||||
|     }); | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| export const getAppConfig: GetAppConfig = (appData) => { | ||||
|   return { | ||||
|     auth: { | ||||
|       initialAuth: appData?.auth, | ||||
|     }, | ||||
|   }, | ||||
| }); | ||||
|   }; | ||||
| }; | ||||
|  |  | |||
|  | @ -6,7 +6,6 @@ export default () => { | |||
| 
 | ||||
|   console.log('render Layout', 'data', data, 'config', config); | ||||
| 
 | ||||
| 
 | ||||
|   return ( | ||||
|     <div> | ||||
|       <h1>Layout</h1> | ||||
|  |  | |||
|  | @ -1,3 +1,3 @@ | |||
| import { defineAppConfig } from 'ice'; | ||||
| 
 | ||||
| export default defineAppConfig({}); | ||||
| export function getAppConfig() { | ||||
|   return {}; | ||||
| } | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| import { dataLoader } from '@ice/runtime'; | ||||
| import appConfig from '@/app'; | ||||
| import { getAppData } from '@/app'; | ||||
| 
 | ||||
| <%- loaders %> | ||||
| 
 | ||||
| loaders['__app'] = appConfig?.app.getData; | ||||
| loaders['__app'] = getAppData; | ||||
| 
 | ||||
| dataLoader.init(loaders); | ||||
|  | @ -1,11 +1,11 @@ | |||
| import { runClientApp } from '@ice/runtime'; | ||||
| import appConfig from '@/app'; | ||||
| import * as app from '@/app'; | ||||
| import runtimeModules from './runtimeModules'; | ||||
| import routes from './routes'; | ||||
| import Document from '@/document'; | ||||
| 
 | ||||
| runClientApp({ | ||||
|   appConfig, | ||||
|   app, | ||||
|   runtimeModules, | ||||
|   routes, | ||||
|   Document | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ process.env.<%= key %> = __process.env.<%= key %>__; | |||
| <% }) %> | ||||
| 
 | ||||
| import * as runtime from '@ice/runtime/server'; | ||||
| import appConfig from '@/app'; | ||||
| import * as app from '@/app'; | ||||
| import runtimeModules from './runtimeModules'; | ||||
| import Document from '@/document'; | ||||
| import assetsManifest from './assets-manifest.json'; | ||||
|  | @ -13,7 +13,7 @@ import routes from './routes'; | |||
| 
 | ||||
| export async function renderToHTML(requestContext, documentOnly) { | ||||
|   return await runtime.renderToHTML(requestContext, { | ||||
|     appConfig, | ||||
|     app, | ||||
|     assetsManifest, | ||||
|     routes, | ||||
|     runtimeModules, | ||||
|  | @ -24,7 +24,7 @@ export async function renderToHTML(requestContext, documentOnly) { | |||
| 
 | ||||
| export async function renderToResponse(requestContext, documentOnly) { | ||||
|   runtime.renderToResponse(requestContext, { | ||||
|     appConfig, | ||||
|     app, | ||||
|     assetsManifest, | ||||
|     routes, | ||||
|     runtimeModules, | ||||
|  |  | |||
|  | @ -30,7 +30,6 @@ export { | |||
| }; | ||||
| 
 | ||||
| export { | ||||
|   defineAppConfig, | ||||
|   useAppData, | ||||
|   useData, | ||||
|   useConfig, | ||||
|  |  | |||
|  | @ -1,5 +1,7 @@ | |||
| import type { AppConfig as DefaultAppConfig } from '@ice/runtime'; | ||||
| 
 | ||||
| export type { GetAppConfig, GetAppData } from '@ice/runtime'; | ||||
| 
 | ||||
| <%- configTypes.imports %> | ||||
| 
 | ||||
| <% if (configTypes.imports) {%> | ||||
|  | @ -12,4 +14,4 @@ interface ExtendsAppConfig extends DefaultAppConfig { | |||
| export type AppConfig = ExtendsAppConfig; | ||||
| <% } else { %> | ||||
| export type AppConfig = DefaultAppConfig; | ||||
| <% } %> | ||||
| <% } %> | ||||
|  |  | |||
|  | @ -5,9 +5,9 @@ import type { InjectProps } from './Auth'; | |||
| import type { AuthConfig, AuthType } from './types'; | ||||
| 
 | ||||
| const runtime: RuntimePlugin = ({ appContext, useConfig, addProvider, addWrapper }) => { | ||||
|   const { appConfig, appData = {} } = appContext; | ||||
|   const initialAuth = appData.auth || {}; | ||||
|   const { appConfig } = appContext; | ||||
|   const authConfig: AuthConfig = appConfig.auth || {}; | ||||
|   const initialAuth = authConfig.initialAuth || {}; | ||||
| 
 | ||||
|   const AuthProviderWrapper: AppProvider = ({ children }) => { | ||||
|     const [state, setState] = React.useState<AuthType>(initialAuth); | ||||
|  |  | |||
|  | @ -1,6 +1,9 @@ | |||
| import type * as React from 'react'; | ||||
| import type { RouteConfig } from '@ice/types'; | ||||
| export interface AuthConfig { | ||||
|   initialAuth: { | ||||
|     [auth: string]: boolean; | ||||
|   }; | ||||
|   NoAuthFallback?: React.ComponentType<{routeConfig: RouteConfig}>; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import * as React from 'react'; | ||||
| import type { AppConfig, AppData, RequestContext } from './types'; | ||||
| import type { AppEntry, AppData, RequestContext } from './types'; | ||||
| 
 | ||||
| const Context = React.createContext<AppData | undefined>(undefined); | ||||
| 
 | ||||
|  | @ -15,7 +15,7 @@ const AppDataProvider = Context.Provider; | |||
| /** | ||||
|  * Call the getData of app config. | ||||
|  */ | ||||
| async function getAppData(appConfig: AppConfig, requestContext: RequestContext): Promise<AppData> { | ||||
| async function getAppData(appEntry: AppEntry, requestContext: RequestContext): Promise<AppData> { | ||||
|   const hasGlobalLoader = typeof window !== 'undefined' && (window as any).__ICE_DATA_LOADER__; | ||||
| 
 | ||||
|   if (hasGlobalLoader) { | ||||
|  | @ -23,8 +23,8 @@ async function getAppData(appConfig: AppConfig, requestContext: RequestContext): | |||
|     return await load('__app'); | ||||
|   } | ||||
| 
 | ||||
|   if (appConfig?.app.getData) { | ||||
|     return await appConfig.app.getData(requestContext); | ||||
|   if (appEntry?.getAppData) { | ||||
|     return await appEntry.getAppData(requestContext); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import type { AppConfig } from './types'; | ||||
| import type { AppConfig, AppData, AppEntry } from './types'; | ||||
| 
 | ||||
| const defaultAppConfig: AppConfig = { | ||||
|   app: { | ||||
|  | @ -9,7 +9,11 @@ const defaultAppConfig: AppConfig = { | |||
|   }, | ||||
| }; | ||||
| 
 | ||||
| export default function defineAppConfig(appConfig: AppConfig) { | ||||
| export default function getAppConfig(appEntry: AppEntry, appData: AppData): AppConfig { | ||||
|   const appConfig = appEntry.getAppConfig(appData); | ||||
| 
 | ||||
|   const { app, router, ...others } = appConfig; | ||||
| 
 | ||||
|   return { | ||||
|     app: { | ||||
|       ...defaultAppConfig.app, | ||||
|  | @ -19,5 +23,6 @@ export default function defineAppConfig(appConfig: AppConfig) { | |||
|       ...defaultAppConfig.router, | ||||
|       ...(appConfig.router || {}), | ||||
|     }, | ||||
|     ...others, | ||||
|   }; | ||||
| } | ||||
|  | @ -32,8 +32,9 @@ import type { | |||
|   ServerContext, | ||||
|   AppProvider, | ||||
|   RouteWrapper, | ||||
|   GetAppData, | ||||
|   GetAppConfig, | ||||
| } from './types.js'; | ||||
| import defineAppConfig from './defineAppConfig.js'; | ||||
| import { matchRoutes } from './routes.js'; | ||||
| import dataLoader from './dataLoader.js'; | ||||
| 
 | ||||
|  | @ -51,7 +52,6 @@ export { | |||
|   Links, | ||||
|   Scripts, | ||||
|   Main, | ||||
|   defineAppConfig, | ||||
|   // react-router-dom API
 | ||||
|   Link, | ||||
|   Outlet, | ||||
|  | @ -73,4 +73,6 @@ export type { | |||
|   ServerContext, | ||||
|   AppProvider, | ||||
|   RouteWrapper, | ||||
|   GetAppData, | ||||
|   GetAppConfig, | ||||
| }; | ||||
|  | @ -8,15 +8,16 @@ import App from './App.js'; | |||
| import { AppContextProvider } from './AppContext.js'; | ||||
| import { AppDataProvider, getAppData } from './AppData.js'; | ||||
| import type { | ||||
|   AppContext, AppConfig, RouteItem, AppRouterProps, RoutesData, RoutesConfig, | ||||
|   AppContext, AppEntry, RouteItem, AppRouterProps, RoutesData, RoutesConfig, | ||||
|   RouteWrapperConfig, RuntimeModules, RouteMatch, ComponentWithChildren, | ||||
| } from './types'; | ||||
| import { loadRouteModules, loadRoutesData, getRoutesConfig, matchRoutes, filterMatchesToLoad } from './routes.js'; | ||||
| import { updateRoutesConfig } from './routesConfig.js'; | ||||
| import getRequestContext from './requestContext.js'; | ||||
| import getAppConfig from './appConfig.js'; | ||||
| 
 | ||||
| interface RunClientAppOptions { | ||||
|   appConfig: AppConfig; | ||||
|   app: AppEntry; | ||||
|   routes: RouteItem[]; | ||||
|   runtimeModules: RuntimeModules; | ||||
|   Document: ComponentWithChildren<{}>; | ||||
|  | @ -24,7 +25,7 @@ interface RunClientAppOptions { | |||
| 
 | ||||
| export default async function runClientApp(options: RunClientAppOptions) { | ||||
|   const { | ||||
|     appConfig, | ||||
|     app, | ||||
|     routes, | ||||
|     runtimeModules, | ||||
|     Document, | ||||
|  | @ -39,9 +40,11 @@ export default async function runClientApp(options: RunClientAppOptions) { | |||
|   const requestContext = getRequestContext(window.location); | ||||
| 
 | ||||
|   if (!appData) { | ||||
|     appData = await getAppData(appConfig, requestContext); | ||||
|     appData = await getAppData(app, requestContext); | ||||
|   } | ||||
| 
 | ||||
|   const appConfig = getAppConfig(app, appData); | ||||
| 
 | ||||
|   if (!routesData) { | ||||
|     routesData = await loadRoutesData(matches, requestContext); | ||||
|   } | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ import Runtime from './runtime.js'; | |||
| import App from './App.js'; | ||||
| import { AppContextProvider } from './AppContext.js'; | ||||
| import { AppDataProvider, getAppData } from './AppData.js'; | ||||
| import getAppConfig from './appConfig.js'; | ||||
| import { DocumentContextProvider } from './Document.js'; | ||||
| import { loadRouteModules, loadRoutesData, getRoutesConfig, matchRoutes } from './routes.js'; | ||||
| import { piperToString, renderToNodeStream } from './server/streamRender.js'; | ||||
|  | @ -14,13 +15,13 @@ import { createStaticNavigator } from './server/navigator.js'; | |||
| import type { NodeWritablePiper } from './server/streamRender.js'; | ||||
| import type { | ||||
|   AppContext, RouteItem, ServerContext, | ||||
|   AppConfig, RuntimePlugin, CommonJsRuntime, AssetsManifest, | ||||
|   AppEntry, RuntimePlugin, CommonJsRuntime, AssetsManifest, | ||||
|   ComponentWithChildren, | ||||
| } from './types'; | ||||
| import getRequestContext from './requestContext.js'; | ||||
| 
 | ||||
| interface RenderOptions { | ||||
|   appConfig: AppConfig; | ||||
|   app: AppEntry; | ||||
|   assetsManifest: AssetsManifest; | ||||
|   routes: RouteItem[]; | ||||
|   runtimeModules: (RuntimePlugin | CommonJsRuntime)[]; | ||||
|  | @ -157,7 +158,7 @@ export async function renderServerEntry( | |||
| ): Promise<RenderResult> { | ||||
|   const { | ||||
|     assetsManifest, | ||||
|     appConfig, | ||||
|     app, | ||||
|     runtimeModules, | ||||
|     routes, | ||||
|     Document, | ||||
|  | @ -165,7 +166,8 @@ export async function renderServerEntry( | |||
| 
 | ||||
|   const requestContext = getRequestContext(location, serverContext); | ||||
| 
 | ||||
|   const appData = await getAppData(appConfig, requestContext); | ||||
|   const appData = await getAppData(app, requestContext); | ||||
|   const appConfig = getAppConfig(app, appData); | ||||
|   const routesData = await loadRoutesData(matches, requestContext); | ||||
|   const routesConfig = getRoutesConfig(matches, routesData); | ||||
| 
 | ||||
|  | @ -232,13 +234,14 @@ export function renderDocument(matches, options: RenderOptions): RenderResult { | |||
|   const { | ||||
|     routes, | ||||
|     assetsManifest, | ||||
|     appConfig, | ||||
|     app, | ||||
|     Document, | ||||
|   } = options; | ||||
| 
 | ||||
|   // renderDocument needn't to load routesData and appData.
 | ||||
|   const appData = null; | ||||
|   const routesData = null; | ||||
|   const appConfig = getAppConfig(app, appData); | ||||
|   const routesConfig = getRoutesConfig(matches, {}); | ||||
| 
 | ||||
|   const appContext: AppContext = { | ||||
|  |  | |||
|  | @ -10,7 +10,6 @@ type AppLifecycle = 'onShow' | 'onHide' | 'onPageNotFound' | 'onShareAppMessage' | |||
| type App = Partial<{ | ||||
|   strict?: boolean; | ||||
|   addProvider?: ({ children }: { children: ReactNode }) => ReactNode; | ||||
|   getData?: GetData; | ||||
| } & Record<AppLifecycle, VoidFunction>>; | ||||
| 
 | ||||
| export type AppData = any; | ||||
|  | @ -28,6 +27,14 @@ export interface RouteConfig { | |||
|   auth?: string[]; | ||||
| } | ||||
| 
 | ||||
| export interface AppEntry { | ||||
|   getAppConfig?: GetAppConfig; | ||||
|   getAppData?: GetAppData; | ||||
| } | ||||
| 
 | ||||
| export type GetAppData = (ctx: RequestContext) => Promise<AppData> | AppData; | ||||
| export type GetAppConfig = (appData: AppData) => AppConfig; | ||||
| 
 | ||||
| // app.getData & route.getData
 | ||||
| export type GetData = (ctx: RequestContext) => Promise<RouteData> | RouteData; | ||||
| // route.getConfig
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue