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) {%>
|
||||
|
|
|
|||
|
|
@ -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