Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Preload all chunks for next/dynamic in SSR #65486

Merged
merged 6 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/next/src/shared/lib/lazy-dynamic/loadable.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Suspense, lazy } from 'react'
import { BailoutToCSR } from './dynamic-bailout-to-csr'
import type { ComponentModule } from './types'
import { PreloadCss } from './preload-css'
import { PreloadChunks } from './preload-chunks'

// Normalize loader to return the module as form { default: Component } for `React.lazy`.
// Also for backward compatible since next/dynamic allows to resolve a component directly with loader
Expand Down Expand Up @@ -52,7 +52,7 @@ function Loadable(options: LoadableOptions) {
<>
{/* During SSR, we need to preload the CSS from the dynamic component to avoid flash of unstyled content */}
{typeof window === 'undefined' ? (
<PreloadCss moduleIds={opts.modules} />
<PreloadChunks moduleIds={opts.modules} />
) : null}
<Lazy {...props} />
</>
Expand Down
64 changes: 64 additions & 0 deletions packages/next/src/shared/lib/lazy-dynamic/preload-chunks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
'use client'

import { getExpectedRequestStore } from '../../../client/components/request-async-storage.external'
import { preload } from 'react-dom'

export function PreloadChunks({
moduleIds,
}: {
moduleIds: string[] | undefined
}) {
// Early return in client compilation and only load requestStore on server side
if (typeof window !== 'undefined') {
return null
}

const requestStore = getExpectedRequestStore('next/dynamic preload')
const allFiles = []

// Search the current dynamic call unique key id in react loadable manifest,
// and find the corresponding CSS files to preload
if (requestStore.reactLoadableManifest && moduleIds) {
const manifest = requestStore.reactLoadableManifest
for (const key of moduleIds) {
if (!manifest[key]) continue
const chunks = manifest[key].files
allFiles.push(...chunks)
}
}

if (allFiles.length === 0) {
return null
}

return (
<>
{allFiles.map((chunk) => {
const href = `${requestStore.assetPrefix}/_next/${encodeURI(chunk)}`
const isCss = chunk.endsWith('.css')
// If it's stylesheet we use `precedence` o help hoist with React Float.
// For stylesheets we actually need to render the CSS because nothing else is going to do it so it needs to be part of the component tree.
// The `preload` for stylesheet is not optional.
if (isCss) {
return (
<link
key={chunk}
// @ts-ignore
precedence="dynamic"
href={href}
rel="stylesheet"
as="style"
/>
)
} else {
// If it's script we use ReactDOM.preload to preload the resources
preload(href, {
as: 'script',
fetchPriority: 'low',
})
return null
}
})}
</>
)
}
47 changes: 0 additions & 47 deletions packages/next/src/shared/lib/lazy-dynamic/preload-css.tsx

This file was deleted.

1 change: 0 additions & 1 deletion packages/next/src/shared/lib/lazy-dynamic/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ export declare type LoaderComponent<P = {}> = Promise<
export declare type Loader<P = {}> = () => LoaderComponent<P>

export type LoadableGeneratedOptions = {
webpack?(): any
modules?: string[]
}

Expand Down
8 changes: 6 additions & 2 deletions test/e2e/app-dir/dynamic-css/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ describe('app dir - dynamic css', () => {
return
}

it('should preload css of dynamic component during SSR', async () => {
it('should preload all chunks of dynamic component during SSR', async () => {
const $ = await next.render$('/ssr')
const cssLinks = $('link[rel="stylesheet"]')
const cssLinks = $('link[rel="stylesheet"][data-precedence="dynamic"]')
expect(cssLinks.attr('href')).toContain('.css')

const preloadJsChunks = $('link[rel="preload"]')
expect(preloadJsChunks.attr('as')).toBe('script')
expect(preloadJsChunks.attr('fetchpriority')).toContain(`low`)
})

it('should only apply corresponding css for page loaded that /ssr', async () => {
Expand Down
Loading