Skip to content

Commit

Permalink
Optimize the SSR middleware runtime size (#30906)
Browse files Browse the repository at this point in the history
By conditionally importing `react-dom/server` under the web runtime and reusing `renderToReadableStream` instead of `renderToStaticMarkup`, we can get rid of the legacy browser React DOM server from the runtime. ~Furthermore we can make the build target `es6` for the SSR middleware, and make some code paths tree-shakable (done in another PR).~

Together this makes the runtime ~32kb smaller.

## Bug

- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Errors have helpful link attached, see `contributing.md`

## Feature

- [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have helpful link attached, see `contributing.md`

## Documentation / Examples

- [ ] Make sure the linting passes by running `yarn lint`
  • Loading branch information
shuding committed Nov 5, 2021
1 parent 6e081e1 commit 0307ba0
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 3 deletions.
8 changes: 8 additions & 0 deletions packages/next/build/webpack-config.ts
Expand Up @@ -676,6 +676,14 @@ export default async function getBaseWebpackConfig(
false,
}
: {}),

...(webServerRuntime
? {
'react-dom/server': dev
? 'react-dom/cjs/react-dom-server.browser.development'
: 'react-dom/cjs/react-dom-server.browser.production.min',
}
: {}),
},
...(targetWeb
? {
Expand Down
36 changes: 33 additions & 3 deletions packages/next/server/render.tsx
Expand Up @@ -2,7 +2,7 @@ import { IncomingMessage, ServerResponse } from 'http'
import { ParsedUrlQuery } from 'querystring'
import type { Writable as WritableType } from 'stream'
import React from 'react'
import * as ReactDOMServer from 'react-dom/server'
import ReactDOMServer from 'react-dom/server'
import { StyleRegistry, createStyleRegistry } from 'styled-jsx'
import { UnwrapPromise } from '../lib/coalesced-function'
import {
Expand Down Expand Up @@ -950,7 +950,12 @@ export async function renderToHTML(
*/
const generateStaticHTML = supportsDynamicHTML !== true
const renderDocument = async () => {
if (Document.getInitialProps) {
if (process.browser && Document.getInitialProps) {
throw new Error(
'`getInitialProps` in Document component is not supported with `concurrentFeatures` enabled.'
)
}
if (!process.browser && Document.getInitialProps) {
const renderPage: RenderPage = (
options: ComponentsEnhancer = {}
): RenderPageResult | Promise<RenderPageResult> => {
Expand Down Expand Up @@ -1120,14 +1125,39 @@ export async function renderToHTML(
styles: documentResult.styles,
useMaybeDeferContent,
}
const documentHTML = ReactDOMServer.renderToStaticMarkup(

const document = (
<AmpStateContext.Provider value={ampState}>
<HtmlContext.Provider value={htmlProps}>
{documentResult.documentElement(htmlProps)}
</HtmlContext.Provider>
</AmpStateContext.Provider>
)

let documentHTML: string
if (process.browser) {
// There is no `renderToStaticMarkup` exposed in the web environment, use
// blocking `renderToReadableStream` to get the similar result.
let result = ''
const readable = (ReactDOMServer as any).renderToReadableStream(document, {
onError: (e: any) => {
throw e
},
})
const reader = readable.getReader()
const decoder = new TextDecoder()
while (true) {
const { done, value } = await reader.read()
if (done) {
break
}
result += typeof value === 'string' ? value : decoder.decode(value)
}
documentHTML = result
} else {
documentHTML = ReactDOMServer.renderToStaticMarkup(document)
}

if (process.env.NODE_ENV !== 'production') {
const nonRenderedComponents = []
const expectedDocComponents = ['Main', 'Head', 'NextScript', 'Html']
Expand Down

0 comments on commit 0307ba0

Please sign in to comment.