@umijs/renderer-react 4.0.0-canary.20241023.1 → 4.0.0-canary.20250731.1

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.
package/dist/browser.d.ts CHANGED
@@ -69,6 +69,10 @@ export declare type RenderClientOpts = {
69
69
  * ssr 的配置
70
70
  */
71
71
  hydrate?: boolean;
72
+ /**
73
+ * ssr 是否启用流式渲染, 默认 true, 对 SEO 存在一定的负优化
74
+ */
75
+ useStream?: boolean;
72
76
  /**
73
77
  * 直接返回组件,是为了方便测试
74
78
  */
package/dist/browser.js CHANGED
@@ -90,7 +90,8 @@ var getBrowser = function getBrowser(opts, routesElement) {
90
90
  routesById: opts.routes,
91
91
  routeComponents: opts.routeComponents,
92
92
  loadingComponent: opts.loadingComponent,
93
- reactRouter5Compat: opts.reactRouter5Compat
93
+ reactRouter5Compat: opts.reactRouter5Compat,
94
+ useStream: opts.useStream
94
95
  });
95
96
  opts.pluginManager.applyPlugins({
96
97
  key: 'patchClientRoutes',
@@ -145,38 +146,14 @@ var getBrowser = function getBrowser(opts, routesElement) {
145
146
  })) || []).filter(Boolean);
146
147
  matchedRouteIds.forEach(function (id) {
147
148
  var _opts$routes$id, _opts$routes$id2;
148
- // preload
149
- // @ts-ignore
150
- var manifest = window.__umi_manifest__;
151
- if (manifest) {
152
- var routeIdReplaced = id.replace(/[\/\-]/g, '_');
153
- var preloadId = "preload-".concat(routeIdReplaced, ".js");
154
- if (!document.getElementById(preloadId)) {
155
- var keys = Object.keys(manifest).filter(function (k) {
156
- return k.startsWith(routeIdReplaced + '.');
157
- });
158
- keys.forEach(function (key) {
159
- if (!/\.(js|css)$/.test(key)) {
160
- throw Error("preload not support ".concat(key, " file"));
161
- }
162
- var file = manifest[key];
163
- var link = document.createElement('link');
164
- link.rel = 'preload';
165
- link.as = 'style';
166
- if (key.endsWith('.js')) {
167
- link.as = 'script';
168
- link.id = preloadId;
169
- }
170
- // publicPath already in the manifest,
171
- // but if runtimePublicPath is true, we need to replace it
172
- if (opts.runtimePublicPath) {
173
- file = file.replace(new RegExp("^".concat(opts.publicPath)),
174
- // @ts-ignore
175
- window.publicPath);
176
- }
177
- link.href = file;
178
- document.head.appendChild(link);
179
- });
149
+ // preload lazy component
150
+ // window.__umi_route_prefetch__ is available when routePrefetch and manifest config is enabled
151
+ if (window.__umi_route_prefetch__) {
152
+ var _opts$routeComponents;
153
+ // ref: https://github.com/facebook/react/blob/0940414/packages/react/src/ReactLazy.js#L135
154
+ var lazyCtor = (_opts$routeComponents = opts.routeComponents[id]) === null || _opts$routeComponents === void 0 || (_opts$routeComponents = _opts$routeComponents._payload) === null || _opts$routeComponents === void 0 ? void 0 : _opts$routeComponents._result;
155
+ if (typeof lazyCtor == 'function') {
156
+ lazyCtor();
180
157
  }
181
158
  }
182
159
  var clientLoader = (_opts$routes$id = opts.routes[id]) === null || _opts$routes$id === void 0 ? void 0 : _opts$routes$id.clientLoader;
package/dist/html.js CHANGED
@@ -101,8 +101,7 @@ var HydrateMetadata = function HydrateMetadata(props) {
101
101
  dangerouslySetInnerHTML: {
102
102
  __html: content
103
103
  },
104
- key: key,
105
- crossOrigin: "anonymous"
104
+ key: key
106
105
  }, rest));
107
106
  }));
108
107
  };
@@ -117,7 +116,9 @@ export function Html(_ref) {
117
116
  // TODO: 处理 head 标签,比如 favicon.ico 的一致性
118
117
  // TODO: root 支持配置
119
118
  if (__INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED !== null && __INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED !== void 0 && __INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.pureHtml) {
120
- return /*#__PURE__*/React.createElement("html", null, /*#__PURE__*/React.createElement("head", null), /*#__PURE__*/React.createElement("body", null, /*#__PURE__*/React.createElement(EnableJsScript, null), /*#__PURE__*/React.createElement("div", {
119
+ return /*#__PURE__*/React.createElement("html", null, /*#__PURE__*/React.createElement("head", null, /*#__PURE__*/React.createElement(HydrateMetadata, {
120
+ htmlPageOpts: htmlPageOpts
121
+ })), /*#__PURE__*/React.createElement("body", null, /*#__PURE__*/React.createElement(EnableJsScript, null), /*#__PURE__*/React.createElement("div", {
121
122
  id: mountElementId
122
123
  }, children), /*#__PURE__*/React.createElement(GlobalDataScript, {
123
124
  manifest: manifest,
@@ -160,8 +161,7 @@ export function Html(_ref) {
160
161
  dangerouslySetInnerHTML: {
161
162
  __html: content
162
163
  },
163
- key: key,
164
- crossOrigin: "anonymous"
164
+ key: key
165
165
  }, rest));
166
166
  })))
167
167
  );
package/dist/link.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- import React, { PropsWithChildren } from 'react';
1
+ import React from 'react';
2
2
  import { LinkProps } from 'react-router-dom';
3
- export declare function LinkWithPrefetch(props: PropsWithChildren<{
4
- prefetch?: boolean;
5
- } & LinkProps & React.RefAttributes<HTMLAnchorElement>>): JSX.Element | null;
3
+ export declare const LinkWithPrefetch: React.ForwardRefExoticComponent<Omit<React.PropsWithChildren<{
4
+ prefetch?: boolean | "viewport" | "render" | "intent" | "none" | undefined;
5
+ prefetchTimeout?: number | undefined;
6
+ } & LinkProps & React.RefAttributes<HTMLAnchorElement>>, "ref"> & React.RefAttributes<unknown>>;
package/dist/link.js CHANGED
@@ -1,22 +1,89 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
3
3
  var _excluded = ["prefetch"];
4
- import React from 'react';
4
+ import React, { useLayoutEffect } from 'react';
5
5
  import { Link } from 'react-router-dom';
6
6
  import { useAppData } from "./appContext";
7
- export function LinkWithPrefetch(props) {
7
+ import { useIntersectionObserver } from "./useIntersectionObserver";
8
+ function useForwardedRef(ref) {
9
+ var innerRef = React.useRef(null);
10
+ React.useEffect(function () {
11
+ if (!ref) return;
12
+ if (typeof ref === 'function') {
13
+ ref(innerRef.current);
14
+ } else {
15
+ ref.current = innerRef.current;
16
+ }
17
+ });
18
+ return innerRef;
19
+ }
20
+ export var LinkWithPrefetch = /*#__PURE__*/React.forwardRef(function (props, forwardedRef) {
8
21
  var _props$to;
9
- var prefetch = props.prefetch,
22
+ var prefetchProp = props.prefetch,
10
23
  linkProps = _objectWithoutProperties(props, _excluded);
24
+ var _ref = typeof window !== 'undefined' &&
25
+ // @ts-ignore
26
+ window.__umi_route_prefetch__ || {
27
+ defaultPrefetch: 'none',
28
+ defaultPrefetchTimeout: 50
29
+ },
30
+ defaultPrefetch = _ref.defaultPrefetch,
31
+ defaultPrefetchTimeout = _ref.defaultPrefetchTimeout;
32
+ var prefetch = (prefetchProp === true ? 'intent' : prefetchProp === false ? 'none' : prefetchProp) || defaultPrefetch;
33
+ if (!['intent', 'render', 'viewport', 'none'].includes(prefetch)) {
34
+ throw new Error("Invalid prefetch value ".concat(prefetch, " found in Link component"));
35
+ }
11
36
  var appData = useAppData();
12
37
  var to = typeof props.to === 'string' ? props.to : (_props$to = props.to) === null || _props$to === void 0 ? void 0 : _props$to.pathname;
38
+ var hasRenderFetched = React.useRef(false);
39
+ var ref = useForwardedRef(forwardedRef);
40
+ // prefetch intent
41
+ var handleMouseEnter = function handleMouseEnter(e) {
42
+ if (prefetch !== 'intent') return;
43
+ var eventTarget = e.target || {};
44
+ if (eventTarget.preloadTimeout) return;
45
+ eventTarget.preloadTimeout = setTimeout(function () {
46
+ var _appData$preloadRoute;
47
+ eventTarget.preloadTimeout = null;
48
+ (_appData$preloadRoute = appData.preloadRoute) === null || _appData$preloadRoute === void 0 || _appData$preloadRoute.call(appData, to);
49
+ }, props.prefetchTimeout || defaultPrefetchTimeout);
50
+ };
51
+ var handleMouseLeave = function handleMouseLeave(e) {
52
+ if (prefetch !== 'intent') return;
53
+ var eventTarget = e.target || {};
54
+ if (eventTarget.preloadTimeout) {
55
+ clearTimeout(eventTarget.preloadTimeout);
56
+ eventTarget.preloadTimeout = null;
57
+ }
58
+ };
59
+
60
+ // prefetch render
61
+ useLayoutEffect(function () {
62
+ if (prefetch === 'render' && !hasRenderFetched.current) {
63
+ var _appData$preloadRoute2;
64
+ (_appData$preloadRoute2 = appData.preloadRoute) === null || _appData$preloadRoute2 === void 0 || _appData$preloadRoute2.call(appData, to);
65
+ hasRenderFetched.current = true;
66
+ }
67
+ }, [prefetch, to]);
68
+
69
+ // prefetch viewport
70
+ useIntersectionObserver(ref, function (entry) {
71
+ if (entry !== null && entry !== void 0 && entry.isIntersecting) {
72
+ var _appData$preloadRoute3;
73
+ (_appData$preloadRoute3 = appData.preloadRoute) === null || _appData$preloadRoute3 === void 0 || _appData$preloadRoute3.call(appData, to);
74
+ }
75
+ }, {
76
+ rootMargin: '100px'
77
+ }, {
78
+ disabled: prefetch !== 'viewport'
79
+ });
80
+
13
81
  // compatible with old code
14
82
  // which to might be undefined
15
83
  if (!to) return null;
16
84
  return /*#__PURE__*/React.createElement(Link, _extends({
17
- onMouseEnter: function onMouseEnter() {
18
- var _appData$preloadRoute;
19
- return prefetch && to && ((_appData$preloadRoute = appData.preloadRoute) === null || _appData$preloadRoute === void 0 ? void 0 : _appData$preloadRoute.call(appData, to));
20
- }
85
+ onMouseEnter: handleMouseEnter,
86
+ onMouseLeave: handleMouseLeave,
87
+ ref: ref
21
88
  }, linkProps), props.children);
22
- }
89
+ });
package/dist/routes.d.ts CHANGED
@@ -6,4 +6,5 @@ export declare function createClientRoutes(opts: {
6
6
  parentId?: string;
7
7
  loadingComponent?: React.ReactNode;
8
8
  reactRouter5Compat?: boolean;
9
+ useStream?: boolean;
9
10
  }): IClientRoute[];
package/dist/routes.js CHANGED
@@ -10,11 +10,13 @@ import { RouteContext, useRouteData } from "./routeContext";
10
10
  export function createClientRoutes(opts) {
11
11
  var routesById = opts.routesById,
12
12
  parentId = opts.parentId,
13
- routeComponents = opts.routeComponents;
13
+ routeComponents = opts.routeComponents,
14
+ _opts$useStream = opts.useStream,
15
+ useStream = _opts$useStream === void 0 ? true : _opts$useStream;
14
16
  return Object.keys(routesById).filter(function (id) {
15
17
  return routesById[id].parentId === parentId;
16
18
  }).map(function (id) {
17
- var route = createClientRoute(_objectSpread({
19
+ var route = createClientRoute(_objectSpread(_objectSpread({
18
20
  route: routesById[id],
19
21
  routeComponent: routeComponents[id],
20
22
  loadingComponent: opts.loadingComponent,
@@ -24,13 +26,16 @@ export function createClientRoutes(opts) {
24
26
  hasChildren: Object.keys(routesById).filter(function (rid) {
25
27
  return routesById[rid].parentId === id;
26
28
  }).length > 0
29
+ }), {}, {
30
+ useStream: useStream
27
31
  }));
28
32
  var children = createClientRoutes({
29
33
  routesById: routesById,
30
34
  routeComponents: routeComponents,
31
35
  parentId: route.id,
32
36
  loadingComponent: opts.loadingComponent,
33
- reactRouter5Compat: opts.reactRouter5Compat
37
+ reactRouter5Compat: opts.reactRouter5Compat,
38
+ useStream: useStream
34
39
  });
35
40
  if (children.length > 0) {
36
41
  route.children = children;
@@ -58,7 +63,9 @@ function NavigateWithParams(props) {
58
63
  }, propsWithParams));
59
64
  }
60
65
  function createClientRoute(opts) {
61
- var route = opts.route;
66
+ var route = opts.route,
67
+ _opts$useStream2 = opts.useStream,
68
+ useStream = _opts$useStream2 === void 0 ? true : _opts$useStream2;
62
69
  var redirect = route.redirect,
63
70
  props = _objectWithoutProperties(route, _excluded);
64
71
  var Remote = opts.reactRouter5Compat ? RemoteComponentReactRouter5 : RemoteComponent;
@@ -72,7 +79,8 @@ function createClientRoute(opts) {
72
79
  }, /*#__PURE__*/React.createElement(Remote, {
73
80
  loader: /*#__PURE__*/React.memo(opts.routeComponent),
74
81
  loadingComponent: opts.loadingComponent || DefaultLoading,
75
- hasChildren: opts.hasChildren
82
+ hasChildren: opts.hasChildren,
83
+ useStream: useStream
76
84
  }))
77
85
  }, props);
78
86
  }
@@ -95,20 +103,21 @@ function RemoteComponentReactRouter5(props) {
95
103
 
96
104
  // staticContext 没有兼容 好像没看到对应的兼容写法
97
105
  var Component = props.loader;
98
- return /*#__PURE__*/React.createElement(React.Suspense, {
99
- fallback: /*#__PURE__*/React.createElement(props.loadingComponent, null)
100
- }, /*#__PURE__*/React.createElement(Component, {
106
+ var ComponentProps = {
101
107
  location: history.location,
102
108
  match: match,
103
109
  history: history,
104
110
  params: params,
105
111
  route: route,
106
112
  routes: clientRoutes
107
- }, props.hasChildren && /*#__PURE__*/React.createElement(Outlet, null)));
113
+ };
114
+ return props.useStream ? /*#__PURE__*/React.createElement(React.Suspense, {
115
+ fallback: /*#__PURE__*/React.createElement(props.loadingComponent, null)
116
+ }, /*#__PURE__*/React.createElement(Component, ComponentProps, props.hasChildren && /*#__PURE__*/React.createElement(Outlet, null))) : /*#__PURE__*/React.createElement(Component, ComponentProps, props.hasChildren && /*#__PURE__*/React.createElement(Outlet, null));
108
117
  }
109
118
  function RemoteComponent(props) {
110
119
  var Component = props.loader;
111
- return /*#__PURE__*/React.createElement(React.Suspense, {
120
+ return props.useStream ? /*#__PURE__*/React.createElement(React.Suspense, {
112
121
  fallback: /*#__PURE__*/React.createElement(props.loadingComponent, null)
113
- }, /*#__PURE__*/React.createElement(Component, null));
122
+ }, /*#__PURE__*/React.createElement(Component, null)) : /*#__PURE__*/React.createElement(Component, null);
114
123
  }
package/dist/server.js CHANGED
@@ -21,7 +21,8 @@ function _getClientRootComponent() {
21
21
  components = _objectSpread({}, opts.routeComponents); // todo 参数对齐
22
22
  clientRoutes = createClientRoutes({
23
23
  routesById: opts.routes,
24
- routeComponents: components
24
+ routeComponents: components,
25
+ useStream: opts.useStream
25
26
  });
26
27
  opts.pluginManager.applyPlugins({
27
28
  key: 'patchClientRoutes',
package/dist/types.d.ts CHANGED
@@ -65,6 +65,7 @@ export interface IRootComponentOptions extends IHtmlHydrateOptions {
65
65
  };
66
66
  manifest: any;
67
67
  basename?: string;
68
+ useStream?: boolean;
68
69
  }
69
70
  export interface IHtmlProps extends IHtmlHydrateOptions {
70
71
  children?: React.ReactNode;
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ export declare function useIntersectionObserver<T extends Element>(ref: React.RefObject<T>, callback: (entry: IntersectionObserverEntry | undefined) => void, intersectionObserverOptions?: IntersectionObserverInit, options?: {
3
+ disabled?: boolean;
4
+ }): IntersectionObserver | null;
@@ -0,0 +1,26 @@
1
+ import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
2
+ import React from 'react';
3
+ export function useIntersectionObserver(ref, callback) {
4
+ var intersectionObserverOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
5
+ var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
6
+ // check if IntersectionObserver is available
7
+ if (typeof IntersectionObserver !== 'function') return null;
8
+ var isIntersectionObserverAvailable = React.useRef(typeof IntersectionObserver === 'function');
9
+ var observerRef = React.useRef(null);
10
+ React.useEffect(function () {
11
+ if (!ref.current || !isIntersectionObserverAvailable.current || options.disabled) {
12
+ return;
13
+ }
14
+ observerRef.current = new IntersectionObserver(function (_ref) {
15
+ var _ref2 = _slicedToArray(_ref, 1),
16
+ entry = _ref2[0];
17
+ callback(entry);
18
+ }, intersectionObserverOptions);
19
+ observerRef.current.observe(ref.current);
20
+ return function () {
21
+ var _observerRef$current;
22
+ (_observerRef$current = observerRef.current) === null || _observerRef$current === void 0 || _observerRef$current.disconnect();
23
+ };
24
+ }, [callback, intersectionObserverOptions, options.disabled, ref]);
25
+ return observerRef.current;
26
+ }
@@ -14,4 +14,4 @@ export interface RouteComponentProps<T = ReturnType<typeof useParams>> {
14
14
  params?: T;
15
15
  navigate?: ReturnType<typeof useNavigate>;
16
16
  }
17
- export declare function withRouter<P extends RouteComponentProps<P>>(Component: React.ComponentType<P>): (props: P) => JSX.Element;
17
+ export declare function withRouter<P extends RouteComponentProps<P>>(Component: React.ComponentType<P>): (props: P) => React.JSX.Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umijs/renderer-react",
3
- "version": "4.0.0-canary.20241023.1",
3
+ "version": "4.0.0-canary.20250731.1",
4
4
  "description": "@umijs/renderer-react",
5
5
  "homepage": "https://github.com/umijs/umi/tree/master/packages/renderer-react#readme",
6
6
  "bugs": "https://github.com/umijs/umi/issues",