();
useServerInsertedHTML(() => {
- return inserted html
;
+ return (
+
+ );
});
+ const id = useId();
+
return (
Hello~
+
id: {id}
This is index.tsx
I should be pink
I should be cyan
+
/users/user
@@ -43,19 +58,21 @@ export default function HomePage() {
client loader data: {JSON.stringify(clientLoaderData)}
server loader data: {JSON.stringify(serverLoaderData)}
+ merge loader data: {JSON.stringify(loaderData)}
);
}
-export async function clientLoader() {
+export const clientLoader: ClientLoader = async ({}) => {
await new Promise((resolve) => setTimeout(resolve, Math.random() * 1000));
- return { message: 'data from client loader of index.tsx' };
-}
+ return { clientMessage: 'data from client loader of index.tsx' };
+};
+clientLoader.hydrate = true;
export const serverLoader: ServerLoader = async (req) => {
- const url = req!.request.url;
+ const url = req?.request?.url;
await new Promise((resolve) => setTimeout(resolve, Math.random() * 1000));
- return { message: `data from server loader of index.tsx, url: ${url}` };
+ return { serverMessage: `data from server loader of index.tsx, url: ${url}` };
};
// SEO-设置页面的TDK
diff --git a/index.js b/index.js
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/package.json b/package.json
index 5823647d8fa1..23501db0a4e4 100644
--- a/package.json
+++ b/package.json
@@ -66,8 +66,8 @@
"@types/jest": "^29.2.5",
"@types/node": "^18.11.18",
"@types/qs": "^6.9.7",
- "@types/react": "^18.0.26",
- "@types/react-dom": "^18.0.10",
+ "@types/react": "^18.3.3",
+ "@types/react-dom": "^18.3.0",
"@types/resolve": "^1.20.2",
"@types/rimraf": "3.0.2",
"@types/tunnel": "^0.0.3",
@@ -101,8 +101,8 @@
"prettier-plugin-organize-imports": "^3.2.2",
"prettier-plugin-packagejson": "^2.4.3",
"qs": "^6.11.0",
- "react": "18.1.0",
- "react-dom": "18.1.0",
+ "react": "18.3.1",
+ "react-dom": "18.3.1",
"react-text-loop-next": "0.0.3",
"regenerator-runtime": "^0.13.11",
"resolve": "^1.22.0",
@@ -142,8 +142,8 @@
]
},
"overrides": {
- "browserslist": "$browserslist",
- "@parcel/watcher": "2.1.0"
+ "@parcel/watcher": "2.1.0",
+ "browserslist": "$browserslist"
}
},
"_local": "This flag is used to check if the development in local, Please do not delete."
diff --git a/packages/bundler-webpack/src/config/definePlugin.test.ts b/packages/bundler-webpack/src/config/definePlugin.test.ts
index ad52951ce0ed..b31eb7f53a56 100644
--- a/packages/bundler-webpack/src/config/definePlugin.test.ts
+++ b/packages/bundler-webpack/src/config/definePlugin.test.ts
@@ -13,6 +13,7 @@ test('normal', () => {
NODE_ENV: '"test"',
PUBLIC_PATH: '"/"',
},
+ 'process.env.SSR_MANIFEST': 'process.env.SSR_MANIFEST',
});
});
@@ -31,6 +32,7 @@ test('env variables', () => {
UMI_APP_FOO: '"BAR"',
PUBLIC_PATH: '"/"',
},
+ 'process.env.SSR_MANIFEST': 'process.env.SSR_MANIFEST',
});
});
diff --git a/packages/bundler-webpack/src/config/definePlugin.ts b/packages/bundler-webpack/src/config/definePlugin.ts
index 545a8db71aef..9e22c0b5db0e 100644
--- a/packages/bundler-webpack/src/config/definePlugin.ts
+++ b/packages/bundler-webpack/src/config/definePlugin.ts
@@ -66,6 +66,7 @@ export function resolveDefine(opts: IOpts) {
return {
'process.env': env,
+ 'process.env.SSR_MANIFEST': 'process.env.SSR_MANIFEST',
...define,
};
}
diff --git a/packages/mfsu/src/mfsu/strategyStaticAnalyze.ts b/packages/mfsu/src/mfsu/strategyStaticAnalyze.ts
index ac0901f8e421..a573d46c649b 100644
--- a/packages/mfsu/src/mfsu/strategyStaticAnalyze.ts
+++ b/packages/mfsu/src/mfsu/strategyStaticAnalyze.ts
@@ -1,11 +1,11 @@
import { logger, printHelp, winPath } from '@umijs/utils';
+import type { Configuration } from 'webpack';
import { checkMatch } from '../babelPlugins/awaitImport/checkMatch';
import mfImport from '../babelPlugins/awaitImport/MFImport';
import { StaticDepInfo } from '../staticDepInfo/staticDepInfo';
+import { extractBabelPluginImportOptions } from '../utils/webpackUtils';
import { IBuildDepPluginOpts } from '../webpackPlugins/buildDepPlugin';
import type { IMFSUStrategy, MFSU } from './mfsu';
-import type { Configuration } from 'webpack';
-import { extractBabelPluginImportOptions } from '../utils/webpackUtils';
export class StaticAnalyzeStrategy implements IMFSUStrategy {
private readonly mfsu: MFSU;
diff --git a/packages/plugins/libs/qiankun/master/masterRuntimePlugin.tsx b/packages/plugins/libs/qiankun/master/masterRuntimePlugin.tsx
index 13520cb78342..d8a70444f40d 100644
--- a/packages/plugins/libs/qiankun/master/masterRuntimePlugin.tsx
+++ b/packages/plugins/libs/qiankun/master/masterRuntimePlugin.tsx
@@ -77,6 +77,10 @@ function patchMicroAppRouteComponent(routes: any[]) {
}
export async function render(oldRender: typeof noop) {
+ // 在 ssr 的场景下,直接返回旧的 render
+ if (typeof window === 'undefined') {
+ return oldRender();
+ }
const runtimeOptions = await getMasterRuntime();
let masterOptions: MasterOptions = {
...getMasterOptions(),
@@ -138,6 +142,10 @@ export async function render(oldRender: typeof noop) {
}
export function patchClientRoutes({ routes }: { routes: any[] }) {
+ // 在 ssr 的场景下,不执行主应用的 patchClientRoutes
+ if (typeof window === 'undefined') {
+ return;
+ }
const microAppRoutes = [].concat(
deepFilterLeafRoutes(routes),
deepFilterLeafRoutes(microAppRuntimeRoutes),
diff --git a/packages/plugins/libs/qiankun/slave/slaveRuntimePlugin.ts b/packages/plugins/libs/qiankun/slave/slaveRuntimePlugin.ts
index e16244405db5..bd81f6bfc2e1 100644
--- a/packages/plugins/libs/qiankun/slave/slaveRuntimePlugin.ts
+++ b/packages/plugins/libs/qiankun/slave/slaveRuntimePlugin.ts
@@ -3,6 +3,10 @@ import { createHistory } from '@@/core/history';
import qiankunRender, { contextOptsStack } from './lifecycles';
export function render(oldRender: any) {
+ // 在 ssr 的场景下,直接返回旧的 render
+ if (typeof window === 'undefined') {
+ return oldRender();
+ }
return qiankunRender().then(oldRender);
}
diff --git a/packages/plugins/src/initial-state.ts b/packages/plugins/src/initial-state.ts
index d265b7640d6f..55c49629a940 100644
--- a/packages/plugins/src/initial-state.ts
+++ b/packages/plugins/src/initial-state.ts
@@ -52,7 +52,7 @@ export default function InitialStateProvider(props: any) {
appLoaded.current = true;
}
}, [loading]);
- if (loading && !appLoaded.current) {
+ if (loading && !appLoaded.current && typeof window !== 'undefined') {
return ;
}
return props.children;
diff --git a/packages/plugins/src/qiankun/master.ts b/packages/plugins/src/qiankun/master.ts
index 29a332190db6..30ff0a914f43 100644
--- a/packages/plugins/src/qiankun/master.ts
+++ b/packages/plugins/src/qiankun/master.ts
@@ -210,4 +210,18 @@ export { MicroAppWithMemoHistory } from './MicroAppWithMemoHistory';
`,
});
});
+
+ api.chainWebpack((config, { ssr }) => {
+ // 在 ssr 的场景下,把 qiankun external 到一个任意模块
+ // 这样就不会把 qiankun 的依赖构建进产物中
+ if (ssr) {
+ const originalExternals = config.get('externals');
+ config.externals({
+ ...originalExternals,
+ qiankun: 'fs',
+ });
+ }
+
+ return config;
+ });
};
diff --git a/packages/plugins/src/qiankun/slave.ts b/packages/plugins/src/qiankun/slave.ts
index 2e976c28149b..c39b3fa0af76 100644
--- a/packages/plugins/src/qiankun/slave.ts
+++ b/packages/plugins/src/qiankun/slave.ts
@@ -180,7 +180,11 @@ export interface IRuntimeConfig {
];
});
- api.chainWebpack((config) => {
+ api.chainWebpack((config, { ssr }) => {
+ // ssr 场景下,通过 cjs 的方式来使用模块,跳过 umd方式的构建
+ if (ssr) {
+ return;
+ }
assert(api.pkg.name, 'You should have name in package.json.');
// 默认不修改 library chunk 的 name,从而确保可以通过 window[appName] 访问到导出
// mfsu 关闭的时候才可以修改,否则可能导致配合 mfsu 时,子应用的 umd chunk 无法被正确加载
@@ -223,11 +227,14 @@ export interface IRuntimeConfig {
api.addEntryCode(() => [
`
-export const bootstrap = qiankun_genBootstrap(render);
-export const mount = qiankun_genMount('${api.config.mountElementId}');
-export const unmount = qiankun_genUnmount('${api.config.mountElementId}');
-export const update = qiankun_genUpdate();
-if (!window.__POWERED_BY_QIANKUN__) {
+const qiankun_noop = () => new Error('qiankun lifecycle is not available for server runtime!');
+const isServer = typeof window === 'undefined';
+export const bootstrap = isServer ? qiankun_noop: qiankun_genBootstrap(render);
+export const mount = isServer ? qiankun_noop : qiankun_genMount('${api.config.mountElementId}');
+export const unmount = isServer ? qiankun_noop : qiankun_genUnmount('${api.config.mountElementId}');
+export const update = isServer ? qiankun_noop : qiankun_genUpdate();
+// 增加 ssr 的判断
+if (!isServer && !window.__POWERED_BY_QIANKUN__) {
bootstrap().then(mount);
}
`,
diff --git a/packages/plugins/src/utils/modelUtils.test.ts b/packages/plugins/src/utils/modelUtils.test.ts
index 748821e827f3..7bd4e21a6ed0 100644
--- a/packages/plugins/src/utils/modelUtils.test.ts
+++ b/packages/plugins/src/utils/modelUtils.test.ts
@@ -1,5 +1,5 @@
-import { ModelUtils, Model, getNamespace, transformSync } from './modelUtils';
import { chalk } from '@umijs/utils';
+import { getNamespace, Model, ModelUtils, transformSync } from './modelUtils';
test('getNamespace', () => {
expect(getNamespace('/a/b/src/models/foo.ts', '/a/b/src')).toEqual('foo');
@@ -199,7 +199,8 @@ test('TopologicalSort: detect circle', () => {
});
test('transformSync', () => {
- transformSync(`
+ transformSync(
+ `
function prop() {}
export class UseDecorator {
@@ -213,9 +214,11 @@ export class UseDecorator {
console.log(a);
}
}
- `, {
- loader: 'ts',
- sourcemap: false,
- minify: false,
- });
+ `,
+ {
+ loader: 'ts',
+ sourcemap: false,
+ minify: false,
+ },
+ );
});
diff --git a/packages/preset-umi/package.json b/packages/preset-umi/package.json
index 1ca2d41f4789..92e8ef9a7c4e 100644
--- a/packages/preset-umi/package.json
+++ b/packages/preset-umi/package.json
@@ -30,7 +30,7 @@
"@umijs/ast": "workspace:*",
"@umijs/babel-preset-umi": "workspace:*",
"@umijs/bundler-esbuild": "workspace:*",
- "@umijs/bundler-mako": "0.7.2",
+ "@umijs/bundler-mako": "0.7.3",
"@umijs/bundler-utils": "workspace:*",
"@umijs/bundler-vite": "workspace:*",
"@umijs/bundler-webpack": "workspace:*",
@@ -57,8 +57,8 @@
"path-to-regexp": "1.7.0",
"postcss": "^8.4.21",
"postcss-prefix-selector": "1.16.0",
- "react": "18.1.0",
- "react-dom": "18.1.0",
+ "react": "18.3.1",
+ "react-dom": "18.3.1",
"react-router": "6.3.0",
"react-router-dom": "6.3.0",
"regenerator-runtime": "0.13.11"
diff --git a/packages/preset-umi/src/commands/build.ts b/packages/preset-umi/src/commands/build.ts
index 8aeacffe6606..332aa60ea37c 100644
--- a/packages/preset-umi/src/commands/build.ts
+++ b/packages/preset-umi/src/commands/build.ts
@@ -1,3 +1,4 @@
+import type { IServicePluginAPI } from '@umijs/core';
import { getMarkup } from '@umijs/server';
import { chalk, fsExtra, logger, rimraf, semver } from '@umijs/utils';
import { writeFileSync } from 'fs';
@@ -17,7 +18,6 @@ const bundlerWebpack: typeof import('@umijs/bundler-webpack') =
lazyImportFromCurrentPkg('@umijs/bundler-webpack');
const bundlerVite: typeof import('@umijs/bundler-vite') =
lazyImportFromCurrentPkg('@umijs/bundler-vite');
-
export default (api: IApi) => {
api.registerCommand({
name: 'build',
@@ -107,7 +107,10 @@ umi build --clean
react: {
runtime: shouldUseAutomaticRuntime ? 'automatic' : 'classic',
},
- config: api.config,
+ config: {
+ outputPath: api.userConfig.outputPath || 'dist',
+ ...api.config,
+ } as IServicePluginAPI['config'],
cwd: api.cwd,
entry,
...(api.config.vite
@@ -136,8 +139,9 @@ umi build --clean
let stats: any;
if (api.config.vite) {
stats = await bundlerVite.build(opts);
- } else if (process.env.OKAM) {
+ } else if (api.config.mako) {
require('@umijs/bundler-webpack/dist/requireHook');
+ // @ts-ignore
const { build } = require(process.env.OKAM);
stats = await build(opts);
} else {
@@ -174,10 +178,11 @@ umi build --clean
publicPath: api.config.publicPath,
});
const { vite } = api.args;
- const markupArgs = await getMarkupArgs({ api });
+ const args = await getMarkupArgs({ api });
+
const finalMarkUpArgs = {
- ...markupArgs,
- styles: markupArgs.styles.concat(
+ ...args,
+ styles: args.styles.concat(
api.config.vite
? []
: [...(assetsMap['umi.css'] || []).map((src) => ({ src }))],
@@ -185,7 +190,7 @@ umi build --clean
scripts: (api.config.vite
? []
: [...(assetsMap['umi.js'] || []).map((src) => ({ src }))]
- ).concat(markupArgs.scripts),
+ ).concat(args.scripts),
esmScript: !!opts.config.esm || vite,
path: '/',
};
diff --git a/packages/preset-umi/src/commands/dev/createRouteMiddleware.ts b/packages/preset-umi/src/commands/dev/createRouteMiddleware.ts
index 831fb8dd0efa..6afd60433143 100644
--- a/packages/preset-umi/src/commands/dev/createRouteMiddleware.ts
+++ b/packages/preset-umi/src/commands/dev/createRouteMiddleware.ts
@@ -14,8 +14,8 @@ function createRouteMiddleware(opts: { api: IApi }) {
onStats?.(stats);
});
- async function getStats() {
- if (!compiler && process.env.OKAM) {
+ async function getStats(api: IApi) {
+ if (!compiler && api.config.mako) {
return {
compilation: { assets: { 'umi.js': 'umi.js', 'umi.css': 'umi.css' } },
hasErrors: () => false,
@@ -32,12 +32,11 @@ function createRouteMiddleware(opts: { api: IApi }) {
return async (req, res, next) => {
const markupArgs = (await getMarkupArgs(opts)) as any;
let assetsMap: Record = {};
- const stats: any = await getStats();
+ const stats: any = await getStats(opts.api);
assetsMap = getAssetsMap({
stats,
publicPath: opts.api.config.publicPath!,
});
-
const requestHandler = await createRequestHandler({
...markupArgs,
styles: markupArgs.styles.concat(
diff --git a/packages/preset-umi/src/commands/dev/dev.ts b/packages/preset-umi/src/commands/dev/dev.ts
index aaa85944b1bc..02c835a62283 100644
--- a/packages/preset-umi/src/commands/dev/dev.ts
+++ b/packages/preset-umi/src/commands/dev/dev.ts
@@ -357,7 +357,10 @@ PORT=8888 umi dev
react: {
runtime: shouldUseAutomaticRuntime ? 'automatic' : 'classic',
},
- config: api.config,
+ config: {
+ outputPath: api.userConfig.outputPath || 'dist',
+ ...api.config,
+ },
pkg: api.pkg,
cwd: api.cwd,
rootDir: process.cwd(),
@@ -433,8 +436,9 @@ PORT=8888 umi dev
if (enableVite) {
await bundlerVite.dev(opts);
- } else if (process.env.OKAM) {
+ } else if (api.config.mako) {
require('@umijs/bundler-webpack/dist/requireHook');
+ // @ts-ignore
const { dev } = require(process.env.OKAM);
await dev(opts);
} else {
diff --git a/packages/preset-umi/src/features/appData/umiInfo.ts b/packages/preset-umi/src/features/appData/umiInfo.ts
index afc2ded7318c..1a3bbedf1bcc 100644
--- a/packages/preset-umi/src/features/appData/umiInfo.ts
+++ b/packages/preset-umi/src/features/appData/umiInfo.ts
@@ -3,9 +3,11 @@ import type { IApi } from '../../types';
export default (api: IApi) => {
api.addEntryCode(() => [
`
-window.g_umi = {
- version: '${api.appData.umi.version}',
-};
- `,
+ if (typeof window !== 'undefined') {
+ window.g_umi = {
+ version: '${api.appData.umi.version}',
+ };
+ }
+ `,
]);
};
diff --git a/packages/preset-umi/src/features/devTool/devTool.ts b/packages/preset-umi/src/features/devTool/devTool.ts
index ef3c503e7ab6..cfd774d9b300 100644
--- a/packages/preset-umi/src/features/devTool/devTool.ts
+++ b/packages/preset-umi/src/features/devTool/devTool.ts
@@ -8,7 +8,7 @@ const assetsDir = join(__dirname, '../../../assets');
export default (api: IApi) => {
api.addBeforeMiddlewares(async () => {
- if (process.env.OKAM) return [];
+ if (api.config.mako) return [];
// get loading html
const $ = await api.applyPlugins({
key: 'modifyDevToolLoadingHTML',
diff --git a/packages/preset-umi/src/features/esbuildHelperChecker/esbuildHelperChecker.ts b/packages/preset-umi/src/features/esbuildHelperChecker/esbuildHelperChecker.ts
index 5c96da0f29b0..1326b19aab6b 100644
--- a/packages/preset-umi/src/features/esbuildHelperChecker/esbuildHelperChecker.ts
+++ b/packages/preset-umi/src/features/esbuildHelperChecker/esbuildHelperChecker.ts
@@ -90,8 +90,7 @@ export default (api: IApi) => {
});
api.onBuildComplete(async ({ err }) => {
- if (api.config.vite) return;
- if (process.env.OKAM) return;
+ if (api.config.vite || api.config.mako) return;
if (err) return;
const jsMinifier = api.config.jsMinifier || 'esbuild';
if (jsMinifier !== 'esbuild') return;
diff --git a/packages/preset-umi/src/features/exportStatic/exportStatic.ts b/packages/preset-umi/src/features/exportStatic/exportStatic.ts
index 9ceb55d72b19..ae0d2f9be2b2 100644
--- a/packages/preset-umi/src/features/exportStatic/exportStatic.ts
+++ b/packages/preset-umi/src/features/exportStatic/exportStatic.ts
@@ -64,25 +64,9 @@ async function getPreRenderedHTML(api: IApi, htmlTpl: string, path: string) {
markupRender ??= require(absServerBuildPath(api))._markupGenerator;
try {
- const markup = await markupRender(path);
- const [mainTpl, extraTpl = ''] = markup.split('