@umijs/plugins 4.0.0-rc.2 → 4.0.0-rc.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
5
9
  }) : (function(o, m, k, k2) {
6
10
  if (k2 === undefined) k2 = k;
7
11
  o[k2] = m[k];
@@ -22,16 +26,33 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
22
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
23
27
  };
24
28
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.ModelUtils = exports.Model = void 0;
29
+ exports.ModelUtils = exports.Model = exports.getNamespace = void 0;
26
30
  const parser = __importStar(require("@umijs/bundler-utils/compiled/babel/parser"));
27
31
  const traverse_1 = __importDefault(require("@umijs/bundler-utils/compiled/babel/traverse"));
32
+ const t = __importStar(require("@umijs/bundler-utils/compiled/babel/types"));
28
33
  const esbuild_1 = require("@umijs/bundler-utils/compiled/esbuild");
29
- const utils_1 = require("@umijs/utils");
30
34
  const fs_1 = require("fs");
31
35
  const path_1 = require("path");
36
+ const plugin_utils_1 = require("umi/plugin-utils");
32
37
  const astUtils_1 = require("./astUtils");
38
+ function getNamespace(absFilePath, absSrcPath) {
39
+ const relPath = (0, plugin_utils_1.winPath)((0, path_1.relative)((0, plugin_utils_1.winPath)(absSrcPath), (0, plugin_utils_1.winPath)(absFilePath)));
40
+ const parts = relPath.split('/');
41
+ const dirs = parts.slice(0, -1);
42
+ const file = parts[parts.length - 1];
43
+ // src/pages/foo/models/bar > foo/bar
44
+ const validDirs = dirs.filter((dir) => !['src', 'pages', 'models'].includes(dir));
45
+ let normalizedFile = file;
46
+ normalizedFile = (0, path_1.basename)(file, (0, path_1.extname)(file));
47
+ // foo.model > foo
48
+ if (normalizedFile.endsWith('.model')) {
49
+ normalizedFile = normalizedFile.split('.').slice(0, -1).join('.');
50
+ }
51
+ return [...validDirs, normalizedFile].join('.');
52
+ }
53
+ exports.getNamespace = getNamespace;
33
54
  class Model {
34
- constructor(file, id) {
55
+ constructor(file, absSrcPath, sort, id) {
35
56
  let namespace;
36
57
  let exportName;
37
58
  const [_file, meta] = file.split('#');
@@ -42,8 +63,38 @@ class Model {
42
63
  }
43
64
  this.file = _file;
44
65
  this.id = `model_${id}`;
45
- this.namespace = namespace || (0, path_1.basename)(file, (0, path_1.extname)(file));
66
+ this.namespace = namespace || getNamespace(_file, absSrcPath);
46
67
  this.exportName = exportName || 'default';
68
+ this.deps = sort ? this.findDeps(sort) : [];
69
+ }
70
+ findDeps(sort) {
71
+ const content = (0, fs_1.readFileSync)(this.file, 'utf-8');
72
+ // transform with esbuild first
73
+ // to reduce unexpected ast problem
74
+ const loader = (0, path_1.extname)(this.file).slice(1);
75
+ const result = (0, esbuild_1.transformSync)(content, {
76
+ loader,
77
+ sourcemap: false,
78
+ minify: false,
79
+ });
80
+ // transform with babel
81
+ const deps = new Set();
82
+ const ast = parser.parse(result.code, {
83
+ sourceType: 'module',
84
+ sourceFilename: this.file,
85
+ plugins: [],
86
+ });
87
+ // TODO: use sort
88
+ sort;
89
+ (0, traverse_1.default)(ast, {
90
+ CallExpression: (path) => {
91
+ if (t.isIdentifier(path.node.callee, { name: 'useModel' }) &&
92
+ t.isStringLiteral(path.node.arguments[0])) {
93
+ deps.add(path.node.arguments[0].value);
94
+ }
95
+ },
96
+ });
97
+ return [...deps];
47
98
  }
48
99
  }
49
100
  exports.Model = Model;
@@ -57,7 +108,7 @@ class ModelUtils {
57
108
  getAllModels(opts) {
58
109
  // reset count
59
110
  this.count = 1;
60
- return [
111
+ const models = [
61
112
  ...this.getModels({
62
113
  base: (0, path_1.join)(this.api.paths.absSrcPath, 'models'),
63
114
  pattern: '**/*.{ts,tsx,js,jsx}',
@@ -72,16 +123,67 @@ class ModelUtils {
72
123
  }),
73
124
  ...opts.extraModels,
74
125
  ].map((file) => {
75
- return new Model(file, this.count++);
126
+ return new Model(file, this.api.paths.absSrcPath, opts.sort, this.count++);
127
+ });
128
+ // check duplicate
129
+ const namespaces = models.map((model) => model.namespace);
130
+ if (new Set(namespaces).size !== namespaces.length) {
131
+ throw new Error(`Duplicate namespace in models: ${namespaces.join(', ')}`);
132
+ }
133
+ // sort models by deps
134
+ if (opts.sort) {
135
+ const namespaces = this.getSortedNamespaces(models);
136
+ models.sort((a, b) => namespaces.indexOf(a.namespace) - namespaces.indexOf(b.namespace));
137
+ }
138
+ return models;
139
+ }
140
+ getSortedNamespaces(models) {
141
+ let final = [];
142
+ models.forEach((model, index) => {
143
+ const { deps, namespace } = model;
144
+ if (deps && deps.length) {
145
+ const itemGroup = [...deps, namespace];
146
+ const cannotUse = [namespace];
147
+ for (let i = 0; i <= index; i += 1) {
148
+ if (models[i].deps.filter((v) => cannotUse.includes(v)).length) {
149
+ if (!cannotUse.includes(models[i].namespace)) {
150
+ cannotUse.push(models[i].namespace);
151
+ i = -1;
152
+ }
153
+ }
154
+ }
155
+ const errorList = deps.filter((v) => cannotUse.includes(v));
156
+ if (errorList.length) {
157
+ throw Error(`Circular dependencies: ${namespace} can't use ${errorList.join(', ')}`);
158
+ }
159
+ const intersection = final.filter((v) => itemGroup.includes(v));
160
+ if (intersection.length) {
161
+ // first intersection
162
+ const finalIndex = final.indexOf(intersection[0]);
163
+ // replace with groupItem
164
+ final = final
165
+ .slice(0, finalIndex)
166
+ .concat(itemGroup)
167
+ .concat(final.slice(finalIndex + 1));
168
+ }
169
+ else {
170
+ final.push(...itemGroup);
171
+ }
172
+ }
173
+ if (!final.includes(namespace)) {
174
+ // first occurrence append to the end
175
+ final.push(namespace);
176
+ }
76
177
  });
178
+ return [...new Set(final)];
77
179
  }
78
180
  getModels(opts) {
79
- return utils_1.glob
181
+ return plugin_utils_1.glob
80
182
  .sync(opts.pattern || '**/*.{ts,js}', {
81
183
  cwd: opts.base,
82
184
  absolute: true,
83
185
  })
84
- .map(utils_1.winPath)
186
+ .map(plugin_utils_1.winPath)
85
187
  .filter((file) => {
86
188
  if (/\.d.ts$/.test(file))
87
189
  return false;
@@ -126,11 +228,15 @@ class ModelUtils {
126
228
  const imports = [];
127
229
  const modelProps = [];
128
230
  models.forEach((model) => {
231
+ const fileWithoutExt = (0, plugin_utils_1.winPath)((0, path_1.format)({
232
+ dir: (0, path_1.dirname)(model.file),
233
+ base: (0, path_1.basename)(model.file, (0, path_1.extname)(model.file)),
234
+ }));
129
235
  if (model.exportName !== 'default') {
130
- imports.push(`import { ${model.exportName} as ${model.id} } from '${model.file}';`);
236
+ imports.push(`import { ${model.exportName} as ${model.id} } from '${fileWithoutExt}';`);
131
237
  }
132
238
  else {
133
- imports.push(`import ${model.id} from '${model.file}';`);
239
+ imports.push(`import ${model.id} from '${fileWithoutExt}';`);
134
240
  }
135
241
  modelProps.push(`${model.id}: { namespace: '${model.namespace}', model: ${model.id} },`);
136
242
  });
@@ -139,7 +245,7 @@ ${imports.join('\n')}
139
245
 
140
246
  export const models = {
141
247
  ${modelProps.join('\n')}
142
- }`;
248
+ } as const`;
143
249
  }
144
250
  }
145
251
  exports.ModelUtils = ModelUtils;
@@ -1,10 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.withTmpPath = void 0;
4
- const utils_1 = require("@umijs/utils");
5
4
  const path_1 = require("path");
5
+ const plugin_utils_1 = require("umi/plugin-utils");
6
6
  function withTmpPath(opts) {
7
- return (0, utils_1.winPath)((0, path_1.join)(opts.api.paths.absTmpPath, opts.api.plugin.key && !opts.noPluginDir
7
+ return (0, plugin_utils_1.winPath)((0, path_1.join)(opts.api.paths.absTmpPath, opts.api.plugin.key && !opts.noPluginDir
8
8
  ? `plugin-${opts.api.plugin.key}`
9
9
  : '', opts.path));
10
10
  }
@@ -163,9 +163,9 @@ export const getLocale = () => {
163
163
  // please clear localStorage if you change the baseSeparator config
164
164
  // because changing will break the app
165
165
  const lang =
166
- typeof localStorage !== 'undefined' && useLocalStorage
167
- ? window.localStorage.getItem('umi_locale')
168
- : '';
166
+ navigator.cookieEnabled && typeof localStorage !== 'undefined' && useLocalStorage
167
+ ? window.localStorage.getItem('umi_locale')
168
+ : '';
169
169
  // support baseNavigator, default true
170
170
  let browserLang;
171
171
  {{#BaseNavigator}}
@@ -207,9 +207,9 @@ export const setLocale = (lang: string, realReload: boolean = true) => {
207
207
 
208
208
  const updater = () => {
209
209
  if (getLocale() !== lang) {
210
- if (typeof window.localStorage !== 'undefined' && useLocalStorage) {
211
- window.localStorage.setItem('umi_locale', lang || '');
212
- }
210
+ if (navigator.cookieEnabled && typeof window.localStorage !== 'undefined' && useLocalStorage) {
211
+ window.localStorage.setItem('umi_locale', lang || '');
212
+ }
213
213
  setIntl(lang);
214
214
  if (realReload) {
215
215
  window.location.reload();
package/libs/model.tsx CHANGED
@@ -1,14 +1,25 @@
1
1
  // @ts-ignore
2
+ import type { models as rawModels } from '@@/plugin-model/model';
2
3
  import isEqual from 'fast-deep-equal';
3
4
  import React, { useContext, useEffect, useMemo, useRef, useState } from 'react';
4
5
 
6
+ type Models = typeof rawModels;
7
+
8
+ type GetNamespaces<M> = {
9
+ [K in keyof M]: M[K] extends { namespace: string }
10
+ ? M[K]['namespace']
11
+ : never;
12
+ }[keyof M];
13
+
14
+ type Namespaces = GetNamespaces<Models>;
15
+
5
16
  // @ts-ignore
6
17
  const Context = React.createContext<{ dispatcher: Dispatcher }>(null);
7
18
 
8
19
  class Dispatcher {
9
- callbacks: Record<string, Set<Function>> = {};
10
- data: Record<string, unknown> = {};
11
- update = (namespace: string) => {
20
+ callbacks: Record<Namespaces, Set<Function>> = {};
21
+ data: Record<Namespaces, unknown> = {};
22
+ update = (namespace: Namespaces) => {
12
23
  if (this.callbacks[namespace]) {
13
24
  this.callbacks[namespace].forEach((cb) => {
14
25
  try {
@@ -87,7 +98,34 @@ export function Provider(props: {
87
98
  );
88
99
  }
89
100
 
90
- export function useModel(namespace: string, selector?: any) {
101
+ type GetModelByNamespace<M, N> = {
102
+ [K in keyof M]: M[K] extends { namespace: string; model: unknown }
103
+ ? M[K]['namespace'] extends N
104
+ ? M[K]['model'] extends (...args: any) => any
105
+ ? ReturnType<M[K]['model']>
106
+ : never
107
+ : never
108
+ : never;
109
+ }[keyof M];
110
+
111
+ type Model<N> = GetModelByNamespace<Models, N>;
112
+ type Selector<N, S> = (model: Model<N>) => S;
113
+
114
+ type SelectedModel<N, T> = T extends (...args: any) => any
115
+ ? ReturnType<NonNullable<T>>
116
+ : Model<N>;
117
+
118
+ export function useModel<N extends Namespaces>(namespace: N): Model<N>;
119
+
120
+ export function useModel<N extends Namespaces, S>(
121
+ namespace: N,
122
+ selector: Selector<N, S>,
123
+ ): SelectedModel<N, typeof selector>;
124
+
125
+ export function useModel<N extends Namespaces, S>(
126
+ namespace: N,
127
+ selector?: Selector<N, S>,
128
+ ): SelectedModel<N, typeof selector> {
91
129
  const { dispatcher } = useContext<{ dispatcher: Dispatcher }>(Context);
92
130
  const selectorRef = useRef(selector);
93
131
  selectorRef.current = selector;
@@ -127,7 +165,7 @@ export function useModel(namespace: string, selector?: any) {
127
165
  }
128
166
  };
129
167
 
130
- dispatcher.callbacks[namespace] ||= new Set();
168
+ dispatcher.callbacks[namespace] ||= new Set() as any; // rawModels 是 umi 动态生成的文件,导致前面 callback[namespace] 的类型无法推导出来,所以用 as any 来忽略掉
131
169
  dispatcher.callbacks[namespace].add(handler);
132
170
  dispatcher.update(namespace);
133
171
 
@@ -74,11 +74,11 @@ export const MicroApp = forwardRef(
74
74
  apps = [],
75
75
  lifeCycles: globalLifeCycles,
76
76
  prefetch = true,
77
+ appNameKeyAlias = 'name',
77
78
  ...globalSettings
78
79
  } = getMasterOptions() as MasterOptions;
79
80
 
80
81
  const {
81
- name,
82
82
  settings: settingsFromProps = {},
83
83
  loader,
84
84
  errorBoundary,
@@ -88,6 +88,10 @@ export const MicroApp = forwardRef(
88
88
  ...propsFromParams
89
89
  } = componentProps;
90
90
 
91
+ // 优先使用 alias 名匹配,fallback 到 name 匹配
92
+ const name = componentProps[appNameKeyAlias] || componentProps.name;
93
+ const isCurrentApp = (app: any) => app[appNameKeyAlias] === name || app.name === name;
94
+
91
95
  const [loading, setLoading] = useState(true);
92
96
  const [error, setError] = useState<any>(null);
93
97
  // 未配置自定义 errorBoundary 且开启了 autoCaptureError 场景下,使用插件默认的 errorBoundary,否则使用自定义 errorBoundary
@@ -117,7 +121,7 @@ export const MicroApp = forwardRef(
117
121
 
118
122
  useImperativeHandle(componentRef, () => microAppRef.current);
119
123
 
120
- const appConfig = apps.find((app: any) => app.name === name);
124
+ const appConfig = apps.find((app: any) => isCurrentApp(app));
121
125
  useEffect(() => {
122
126
  if (!appConfig) {
123
127
  setComponentError(
@@ -139,6 +143,13 @@ export const MicroApp = forwardRef(
139
143
  setComponentError(null);
140
144
  setLoading(true);
141
145
  const configuration = {
146
+ fetch(url) {
147
+ return window.fetch(url, {
148
+ headers: {
149
+ accept: 'text/html',
150
+ },
151
+ });
152
+ },
142
153
  globalContext: window,
143
154
  ...globalSettings,
144
155
  ...settingsFromProps,
@@ -167,13 +178,11 @@ export const MicroApp = forwardRef(
167
178
  if (noneMounted) {
168
179
  if (Array.isArray(prefetch)) {
169
180
  const specialPrefetchApps = apps.filter(
170
- (app) => app.name !== name && prefetch.indexOf(app.name) !== -1,
181
+ (app) => !isCurrentApp(app) && (prefetch.indexOf(app[appNameKeyAlias]) !== -1 || prefetch.indexOf(app.name) !== -1)
171
182
  );
172
183
  prefetchApps(specialPrefetchApps, configuration);
173
184
  } else {
174
- const otherNotMountedApps = apps.filter(
175
- (app) => app.name !== name,
176
- );
185
+ const otherNotMountedApps = apps.filter((app) => !isCurrentApp(app));
177
186
  prefetchApps(otherNotMountedApps, configuration);
178
187
  }
179
188
  noneMounted = false;
@@ -5,7 +5,7 @@
5
5
  * @since 2019-06-20
6
6
  */
7
7
 
8
- import { ReactComponentElement } from 'react';
8
+ import React, { ReactComponentElement } from 'react';
9
9
  import type { IRouteProps } from 'umi';
10
10
 
11
11
  export const defaultMountContainerId = 'root-subapp';
@@ -48,6 +48,7 @@ export function patchMicroAppRoute(
48
48
  getMicroAppRouteComponent: (opts: {
49
49
  appName: string;
50
50
  base: string;
51
+ routePath: string;
51
52
  masterHistoryType: string;
52
53
  routeProps?: any;
53
54
  }) => string | ReactComponentElement<any>,
@@ -63,9 +64,9 @@ export function patchMicroAppRoute(
63
64
  const microAppProps =
64
65
  route[`${routeBindingAlias}Props`] || route.microAppProps || {};
65
66
  if (microAppName) {
66
- if (route.routes?.length) {
67
- const childrenRouteHasComponent = route.routes.some(
68
- (r: any) => r.component,
67
+ if (route.children?.length) {
68
+ const childrenRouteHasComponent = route.children.some(
69
+ (r: any) => r.element,
69
70
  );
70
71
  if (childrenRouteHasComponent) {
71
72
  throw new Error(
@@ -74,7 +75,10 @@ export function patchMicroAppRoute(
74
75
  }
75
76
  }
76
77
 
77
- route.exact = false;
78
+ // 自动追加通配符,匹配子应用的路由
79
+ if (!route.path.endsWith('/*')) {
80
+ route.path = route.path.replace(/\/?$/, '/*');
81
+ }
78
82
 
79
83
  const { settings = {}, ...componentProps } = microAppProps;
80
84
  const routeProps = {
@@ -85,10 +89,11 @@ export function patchMicroAppRoute(
85
89
  const opts = {
86
90
  appName: microAppName,
87
91
  base,
92
+ routePath: route.path,
88
93
  masterHistoryType,
89
94
  routeProps,
90
95
  };
91
- route.component = getMicroAppRouteComponent(opts);
96
+ route.element = React.createElement(getMicroAppRouteComponent(opts), null);
92
97
  }
93
98
  }
94
99
 
@@ -100,8 +105,8 @@ const recursiveSearch = (
100
105
  if (routes[i].path === path) {
101
106
  return routes[i];
102
107
  }
103
- if (routes[i].routes && routes[i].routes?.length) {
104
- const found = recursiveSearch(routes[i].routes || [], path);
108
+ if (routes[i].children && routes[i].children?.length) {
109
+ const found = recursiveSearch(routes[i].children || [], path);
105
110
  if (found) {
106
111
  return found;
107
112
  }
@@ -123,8 +128,8 @@ export function insertRoute(routes: IRouteProps[], microAppRoute: IRouteProps) {
123
128
  );
124
129
  }
125
130
  found.exact = false;
126
- found.routes = found.routes || [];
127
- found.routes.push(microAppRoute);
131
+ found.children = found.children || [];
132
+ found.children.push(microAppRoute);
128
133
  } else {
129
134
  throw new Error(
130
135
  `[plugin-qiankun]: path "${microAppRoute.insert}" not found`,
@@ -1,31 +1,20 @@
1
1
  import React from 'react';
2
2
  import { MicroApp } from './MicroApp';
3
- {{#runtimeHistory}}
4
- import { getCreateHistoryOptions } from 'umi';
5
- {{/runtimeHistory}}
6
- import { useLocation } from 'umi';
7
3
 
8
4
  export function getMicroAppRouteComponent(opts: {
9
5
  appName: string;
10
6
  base: string;
7
+ routePath: string;
11
8
  masterHistoryType: string;
12
9
  routeProps?: any;
13
10
  }) {
14
- const { base, masterHistoryType, appName, routeProps } = opts;
15
- const RouteComponent = ({ match }: any) => {
16
- const url = useLocation().pathname;
17
-
11
+ const { base, masterHistoryType, appName, routeProps, routePath } = opts;
12
+ const RouteComponent = () => {
18
13
  // 默认取静态配置的 base
19
14
  let umiConfigBase = base === '/' ? '' : base;
20
15
 
21
- {{#runtimeHistory}}
22
- // 存在 getCreateHistoryOptions 说明当前应用开启了 runtimeHistory,此时取运行时的 history 配置的 basename
23
- const { basename = '/' } = getCreateHistoryOptions();
24
- umiConfigBase = basename === '/' ? '' : basename;
25
- {{/runtimeHistory}}
26
-
27
- let runtimeMatchedBase =
28
- umiConfigBase + (url.endsWith('/') ? url.substr(0, url.length - 1) : url);
16
+ // 拼接子应用挂载路由
17
+ let runtimeMatchedBase = umiConfigBase + routePath.replace('/*', '');
29
18
 
30
19
  {{#dynamicRoot}}
31
20
  // @see https://github.com/umijs/umi/blob/master/packages/preset-built-in/src/plugins/commands/htmlUtils.ts#L102
@@ -34,10 +34,10 @@ function patchMicroAppRouteComponent(routes: any[]) {
34
34
  const rootRoute = routes.find((route) => route.path === '/');
35
35
  if (rootRoute) {
36
36
  // 如果根路由是叶子节点,则直接返回其父节点
37
- if (!rootRoute.routes) {
37
+ if (!rootRoute.children) {
38
38
  return routes;
39
39
  }
40
- return getRootRoutes(rootRoute.routes);
40
+ return getRootRoutes(rootRoute.children);
41
41
  }
42
42
  return routes;
43
43
  };
@@ -50,11 +50,12 @@ function patchMicroAppRouteComponent(routes: any[]) {
50
50
  const patchRoute = (route: any) => {
51
51
  patchMicroAppRoute(route, getMicroAppRouteComponent, {
52
52
  base,
53
+ routePath: route.path,
53
54
  masterHistoryType,
54
55
  routeBindingAlias,
55
56
  });
56
- if (route.routes?.length) {
57
- route.routes.forEach(patchRoute);
57
+ if (route.children?.length) {
58
+ route.children.forEach(patchRoute);
58
59
  }
59
60
  };
60
61
 
@@ -123,7 +124,7 @@ export async function render(oldRender: typeof noop) {
123
124
  }
124
125
  }
125
126
 
126
- export function patchRoutes({ routes }: { routes: any[] }) {
127
+ export function patchClientRoutes({ routes }: { routes: any[] }) {
127
128
  if (microAppRuntimeRoutes) {
128
129
  patchMicroAppRouteComponent(routes);
129
130
  }
@@ -31,6 +31,8 @@ export type MasterOptions = {
31
31
  routeBindingAlias?: string;
32
32
  // 导出的组件别名,默认 MicroApp
33
33
  exportComponentAlias?: string;
34
+ // MicroApp 寻址时使用的应用名唯一键,默认是 name
35
+ appNameKeyAlias?: string;
34
36
  } & FrameworkConfiguration;
35
37
 
36
38
  export type SlaveOptions = {
@@ -1,5 +1,4 @@
1
1
  // @ts-nocheck
2
- /* eslint-disable */
3
2
  __USE_MODEL__;
4
3
  import React from 'react';
5
4
 
@@ -1,5 +1,4 @@
1
1
  // @ts-nocheck
2
- /* eslint-disable */
3
2
  import { getPluginManager } from '@@/core/plugin';
4
3
  import ReactDOM from 'react-dom';
5
4
  import { ApplyPluginsType } from 'umi';
@@ -22,7 +21,7 @@ let render = noop;
22
21
  let hasMountedAtLeastOnce = false;
23
22
 
24
23
  export default () => defer.promise;
25
- export const clientRenderOptsStack: any[] = [];
24
+ export const contextOptsStack: any[] = [];
26
25
 
27
26
  // function normalizeHistory(
28
27
  // history?: 'string' | Record<string, any>,
@@ -79,10 +78,7 @@ export function genMount(mountElementId: string) {
79
78
  // 默认开启
80
79
  // 如果需要手动控制 loading,通过主应用配置 props.autoSetLoading false 可以关闭
81
80
  callback: () => {
82
- if (
83
- props?.autoSetLoading &&
84
- typeof props?.setLoading === 'function'
85
- ) {
81
+ if (props.autoSetLoading && typeof props.setLoading === 'function') {
86
82
  props.setLoading(false);
87
83
  }
88
84
 
@@ -94,9 +90,11 @@ export function genMount(mountElementId: string) {
94
90
  // 支持通过 props 注入 container 来限定子应用 mountElementId 的查找范围
95
91
  // 避免多个子应用出现在同一主应用时出现 mount 冲突
96
92
  rootElement:
97
- props?.container?.querySelector(`#${mountElementId}`) ||
93
+ props.container?.querySelector(`#${mountElementId}`) ||
98
94
  mountElementId,
99
95
 
96
+ basename: props.base,
97
+
100
98
  // 当存在同一个 umi 子应用在同一个页面被多实例渲染的场景时(比如一个页面里,同时展示了这个子应用的多个路由页面)
101
99
  // mount 钩子会被调用多次,但是具体什么时候对应的实例开始 render 则是不定的,即它调用 applyPlugins('modifyClientRenderOpts') 的时机是不确定的
102
100
  // 为了保证每次 applyPlugins('modifyClientRenderOpts') 调用是生成正确的 history,我们需要这里通过闭包上下文维持 mount 调用时的一些配置信息
@@ -111,7 +109,7 @@ export function genMount(mountElementId: string) {
111
109
  // },
112
110
  };
113
111
 
114
- clientRenderOptsStack.push(clientRenderOpts);
112
+ contextOptsStack.push(clientRenderOpts);
115
113
  }
116
114
 
117
115
  // 第一次 mount defer 被 resolve 后umi 会自动触发 render,非第一次 mount 则需手动触发
@@ -121,6 +119,14 @@ export function genMount(mountElementId: string) {
121
119
  defer.resolve();
122
120
  }
123
121
 
122
+ // 如果需要手动控制 loading,通过主应用配置 props.autoSetLoading false 可以关闭
123
+ // 考虑到 react 18 之后 callback 不再准
124
+ // 所以在这里直接返回,而不使用 ReactDOM.render 的第三个参数
125
+ if (typeof props !== 'undefined') {
126
+ if (props.autoSetLoading && typeof props.setLoading === 'function') {
127
+ props.setLoading(false);
128
+ }
129
+ }
124
130
  hasMountedAtLeastOnce = true;
125
131
  };
126
132
  }
@@ -1,5 +1,4 @@
1
1
  // @ts-nocheck
2
- /* eslint-disable */
3
2
  import { useState } from 'react';
4
3
 
5
4
  let initState: any;
@@ -1,21 +1,15 @@
1
1
  // @ts-nocheck
2
- /* eslint-disable */
3
- import qiankunRender from './lifecycles';
2
+ import qiankunRender, { contextOptsStack } from './lifecycles';
4
3
 
5
4
  export function render(oldRender: any) {
6
5
  return qiankunRender().then(oldRender);
7
6
  }
8
7
 
9
- // export function modifyClientRenderOpts(memo: any) {
10
- // // 每次应用 render 的时候会调 modifyClientRenderOpts,这时尝试从队列中取 render 的配置
11
- // const clientRenderOpts = clientRenderOptsStack.shift();
12
- // if (clientRenderOpts) {
13
- // const history = clientRenderOpts.getHistory();
14
- // delete clientRenderOpts.getHistory;
15
- // clientRenderOpts.history = history;
16
- // }
17
- // return {
18
- // ...memo,
19
- // ...clientRenderOpts,
20
- // };
21
- // }
8
+ export function modifyContextOpts(memo: any) {
9
+ // 每次应用 render 的时候会调 modifyClientRenderOpts,这时尝试从队列中取 render 的配置
10
+ const clientRenderOpts = contextOptsStack.shift();
11
+ return {
12
+ ...memo,
13
+ ...clientRenderOpts,
14
+ };
15
+ }