@shuvi/platform-web 2.0.0-dev.33 → 2.0.0-dev.34

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.
@@ -12,6 +12,30 @@ export interface IRouterConfig {
12
12
  export interface PlatformWebCustomConfig {
13
13
  ssr: boolean;
14
14
  router: IRouterConfig;
15
+ /**
16
+ * Run the initial hydration inside `startTransition`.
17
+ *
18
+ * `hydrateRoot` schedules the first hydration on `DefaultLane`, and
19
+ * `includesBlockingLane` counts that as blocking, so React renders it through
20
+ * `renderRootSync` — one uninterruptible task spanning the whole tree, which
21
+ * is the same shape as the legacy `hydrate` it replaced. Suspense boundaries
22
+ * do not break that up either: a boundary the server rendered completely is
23
+ * hydrated inline in the same pass, and only the placeholders emitted by
24
+ * `renderToPipeableStream` are deferred.
25
+ *
26
+ * A transition puts the work on a transition lane, which is not blocking, so
27
+ * React takes `renderRootConcurrent` and yields between units of work. The
28
+ * total CPU is unchanged; it is split into slices the browser can interleave
29
+ * with input, which is what Total Blocking Time measures. The commit phase
30
+ * stays synchronous, so a floor remains.
31
+ *
32
+ * Worth enabling in proportion to how much there is to hydrate: a large
33
+ * server-rendered document gains, a small one does not. Requires React 18 —
34
+ * with an earlier version this option has no effect.
35
+ *
36
+ * Defaults to false for backward compatibility.
37
+ */
38
+ hydrateInTransition?: boolean;
15
39
  routes?: IPageRouteConfig[];
16
40
  conventionRoutes: {
17
41
  include?: string[];
@@ -1,10 +1,22 @@
1
1
  let doRender;
2
2
  if (process.env.__SHUVI__AFTER__REACT__18__) {
3
3
  const { createRoot, hydrateRoot } = require('react-dom/client');
4
+ const { startTransition } = require('react');
4
5
  let renderRoot;
5
6
  doRender = ({ root, appContainer, shouldHydrate }, callback) => {
6
7
  if (shouldHydrate) {
7
- renderRoot = hydrateRoot(appContainer, root);
8
+ if (process.env.__SHUVI__HYDRATE_IN_TRANSITION__) {
9
+ // `hydrateRoot` returns before the work it schedules runs, so the
10
+ // assignment still happens synchronously here and the branch below can
11
+ // reuse the root for client-side navigations. Only the lane changes:
12
+ // a transition lane is not blocking, so the render loop yields.
13
+ startTransition(() => {
14
+ renderRoot = hydrateRoot(appContainer, root);
15
+ });
16
+ }
17
+ else {
18
+ renderRoot = hydrateRoot(appContainer, root);
19
+ }
8
20
  callback === null || callback === void 0 ? void 0 : callback();
9
21
  }
10
22
  else {
@@ -33,6 +33,7 @@ declare global {
33
33
  router: PlatformWebCustomConfig['router'];
34
34
  routes?: PlatformWebCustomConfig['routes'];
35
35
  conventionRoutes: PlatformWebCustomConfig['conventionRoutes'];
36
+ hydrateInTransition?: PlatformWebCustomConfig['hydrateInTransition'];
36
37
  }
37
38
  interface CustomCorePluginHooks {
38
39
  addRoutes: typeof addRoutes;
@@ -95,7 +95,8 @@ const configWebpack = (config, { name, rspack }, context) => {
95
95
  if (name === shared_1.BUNDLER_TARGET_CLIENT) {
96
96
  config.plugin('version-env-plugin').use(rspack.DefinePlugin, [
97
97
  {
98
- 'process.env.__SHUVI__AFTER__REACT__18__': JSON.stringify(isReactVersionAfter18())
98
+ 'process.env.__SHUVI__AFTER__REACT__18__': JSON.stringify(isReactVersionAfter18()),
99
+ 'process.env.__SHUVI__HYDRATE_IN_TRANSITION__': JSON.stringify(Boolean(context.config.hydrateInTransition))
99
100
  }
100
101
  ]);
101
102
  if (context.mode === 'development') {
@@ -12,6 +12,30 @@ export interface IRouterConfig {
12
12
  export interface PlatformWebCustomConfig {
13
13
  ssr: boolean;
14
14
  router: IRouterConfig;
15
+ /**
16
+ * Run the initial hydration inside `startTransition`.
17
+ *
18
+ * `hydrateRoot` schedules the first hydration on `DefaultLane`, and
19
+ * `includesBlockingLane` counts that as blocking, so React renders it through
20
+ * `renderRootSync` — one uninterruptible task spanning the whole tree, which
21
+ * is the same shape as the legacy `hydrate` it replaced. Suspense boundaries
22
+ * do not break that up either: a boundary the server rendered completely is
23
+ * hydrated inline in the same pass, and only the placeholders emitted by
24
+ * `renderToPipeableStream` are deferred.
25
+ *
26
+ * A transition puts the work on a transition lane, which is not blocking, so
27
+ * React takes `renderRootConcurrent` and yields between units of work. The
28
+ * total CPU is unchanged; it is split into slices the browser can interleave
29
+ * with input, which is what Total Blocking Time measures. The commit phase
30
+ * stays synchronous, so a floor remains.
31
+ *
32
+ * Worth enabling in proportion to how much there is to hydrate: a large
33
+ * server-rendered document gains, a small one does not. Requires React 18 —
34
+ * with an earlier version this option has no effect.
35
+ *
36
+ * Defaults to false for backward compatibility.
37
+ */
38
+ hydrateInTransition?: boolean;
15
39
  routes?: IPageRouteConfig[];
16
40
  conventionRoutes: {
17
41
  include?: string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shuvi/platform-web",
3
- "version": "2.0.0-dev.33",
3
+ "version": "2.0.0-dev.34",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/shuvijs/shuvi.git",
@@ -72,15 +72,15 @@
72
72
  },
73
73
  "dependencies": {
74
74
  "@rspack/plugin-react-refresh": "^1.4.3",
75
- "@shuvi/error-overlay": "2.0.0-dev.33",
76
- "@shuvi/hook": "2.0.0-dev.33",
77
- "@shuvi/platform-shared": "2.0.0-dev.33",
78
- "@shuvi/router": "2.0.0-dev.33",
79
- "@shuvi/router-react": "2.0.0-dev.33",
80
- "@shuvi/runtime": "2.0.0-dev.33",
81
- "@shuvi/shared": "2.0.0-dev.33",
82
- "@shuvi/toolpack": "2.0.0-dev.33",
83
- "@shuvi/utils": "2.0.0-dev.33",
75
+ "@shuvi/error-overlay": "2.0.0-dev.34",
76
+ "@shuvi/hook": "2.0.0-dev.34",
77
+ "@shuvi/platform-shared": "2.0.0-dev.34",
78
+ "@shuvi/router": "2.0.0-dev.34",
79
+ "@shuvi/router-react": "2.0.0-dev.34",
80
+ "@shuvi/runtime": "2.0.0-dev.34",
81
+ "@shuvi/shared": "2.0.0-dev.34",
82
+ "@shuvi/toolpack": "2.0.0-dev.34",
83
+ "@shuvi/utils": "2.0.0-dev.34",
84
84
  "content-type": "1.0.4",
85
85
  "core-js": "3.6.5",
86
86
  "doura": "0.0.13",
@@ -98,10 +98,10 @@
98
98
  "whatwg-fetch": "3.0.0"
99
99
  },
100
100
  "peerDependencies": {
101
- "@shuvi/service": "2.0.0-dev.33"
101
+ "@shuvi/service": "2.0.0-dev.34"
102
102
  },
103
103
  "devDependencies": {
104
- "@shuvi/service": "2.0.0-dev.33",
104
+ "@shuvi/service": "2.0.0-dev.34",
105
105
  "@testing-library/react": "^13.2.0",
106
106
  "@types/content-type": "^1.1.5",
107
107
  "@types/ejs": "^3.1.0",