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