Enable code splitting for the web runtime build (#31090)

When using the web runtime with SSR streaming, this PR significantly improves the build speed for large applications when there're large modules shared by most pages.

With another optimization, `react-dom` will now be excluded in the web runtime build if it's imported in the application. It will only take effect in the client bundle.

## 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`
This commit is contained in:
Shu Ding 2021-11-08 13:41:49 +01:00 committed by GitHub
parent 9e03c8d4ed
commit feed67ee36
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 37 additions and 18 deletions

View file

@ -990,7 +990,12 @@ export default async function getBaseWebpackConfig(
? // make sure importing "next" is handled gracefully for client
// bundles in case a user imported types and it wasn't removed
// TODO: should we warn/error for this instead?
['next', ...(webServerRuntime ? [{ etag: '{}', chalk: '{}' }] : [])]
[
'next',
...(webServerRuntime
? [{ etag: '{}', chalk: '{}', 'react-dom': '{}' }]
: []),
]
: !isServerless
? [
({
@ -1054,10 +1059,10 @@ export default async function getBaseWebpackConfig(
}
: {}),
splitChunks: isServer
? dev || webServerRuntime
? dev
? false
: ({
filename: '[name].js',
filename: webServerRuntime ? 'chunks/[name].js' : '[name].js',
// allow to split entrypoints
chunks: ({ name }: any) => !name?.match(MIDDLEWARE_ROUTE),
// size of files is not so relevant for server build
@ -1465,7 +1470,8 @@ export default async function getBaseWebpackConfig(
new PagesManifestPlugin({ serverless: isLikeServerless, dev }),
// MiddlewarePlugin should be after DefinePlugin so NEXT_PUBLIC_*
// replacement is done before its process.env.* handling
!isServer && new MiddlewarePlugin({ dev }),
(!isServer || webServerRuntime) &&
new MiddlewarePlugin({ dev, webServerRuntime }),
isServer && new NextJsSsrImportPlugin(),
!isServer &&
new BuildManifestPlugin({

View file

@ -4,7 +4,6 @@ import { getSortedRoutes } from '../../../shared/lib/router/utils'
import {
MIDDLEWARE_MANIFEST,
MIDDLEWARE_FLIGHT_MANIFEST,
MIDDLEWARE_SSR_RUNTIME_WEBPACK,
MIDDLEWARE_BUILD_MANIFEST,
MIDDLEWARE_REACT_LOADABLE_MANIFEST,
} from '../../../shared/lib/constants'
@ -31,11 +30,25 @@ export interface MiddlewareManifest {
}
}
const middlewareManifest: MiddlewareManifest = {
sortedMiddleware: [],
clientInfo: [],
middleware: {},
version: 1,
}
export default class MiddlewarePlugin {
dev: boolean
webServerRuntime: boolean
constructor({ dev }: { dev: boolean }) {
constructor({
dev,
webServerRuntime,
}: {
dev: boolean
webServerRuntime: boolean
}) {
this.dev = dev
this.webServerRuntime = webServerRuntime
}
createAssets(
@ -44,17 +57,14 @@ export default class MiddlewarePlugin {
envPerRoute: Map<string, string[]>
) {
const entrypoints = compilation.entrypoints
const middlewareManifest: MiddlewareManifest = {
sortedMiddleware: [],
clientInfo: [],
middleware: {},
version: 1,
}
for (const entrypoint of entrypoints.values()) {
if (!entrypoint.name) continue
const result = MIDDLEWARE_FULL_ROUTE_REGEX.exec(entrypoint.name)
const ssrEntryInfo = ssrEntries.get(entrypoint.name)
if (ssrEntryInfo && !this.webServerRuntime) continue
if (!ssrEntryInfo && this.webServerRuntime) continue
const location = result
? `/${result[1]}`
@ -67,14 +77,15 @@ export default class MiddlewarePlugin {
}
const files = ssrEntryInfo
? [
`server/${MIDDLEWARE_SSR_RUNTIME_WEBPACK}.js`,
ssrEntryInfo.requireFlightManifest
? `server/${MIDDLEWARE_FLIGHT_MANIFEST}.js`
: null,
`server/${MIDDLEWARE_BUILD_MANIFEST}.js`,
`server/${MIDDLEWARE_REACT_LOADABLE_MANIFEST}.js`,
`server/${entrypoint.name}.js`,
].filter(nonNullable)
...entrypoint.getFiles().map((file) => 'server/' + file),
]
.filter(nonNullable)
.filter((file: string) => !file.endsWith('.hot-update.js'))
: entrypoint
.getFiles()
.filter((file: string) => !file.endsWith('.hot-update.js'))
@ -106,9 +117,11 @@ export default class MiddlewarePlugin {
}
)
assets[`server/${MIDDLEWARE_MANIFEST}`] = new sources.RawSource(
JSON.stringify(middlewareManifest, null, 2)
)
assets[
this.webServerRuntime
? MIDDLEWARE_MANIFEST
: `server/${MIDDLEWARE_MANIFEST}`
] = new sources.RawSource(JSON.stringify(middlewareManifest, null, 2))
}
apply(compiler: webpack5.Compiler) {