feat(nuxt): enable islands if server pages/components present (#26223)

This commit is contained in:
Daniel Roe 2024-03-13 07:39:35 -07:00 committed by GitHub
parent f080c426a2
commit 9bfd988ca6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 20 additions and 16 deletions

View File

@ -365,10 +365,6 @@ You can define a page as [client only](/docs/guide/directory-structure/component
You can define a page as [server only](/docs/guide/directory-structure/components#server-components) by giving it a `.server.vue` suffix. While you will be able to navigate to the page using client-side navigation, controlled by `vue-router`, it will be rendered with a server component automatically, meaning the code required to render the page will not be in your client-side bundle.
::note
You will also need to enable `experimental.componentIslands` in order to make this possible.
::
## Custom Routing
As your app gets bigger and more complex, your routing might require more flexibility. For this reason, Nuxt directly exposes the router, routes and router options for customization in different ways.

View File

@ -12,10 +12,6 @@ When rendering an island component, the content of the island component is stati
Changing the island component props triggers a refetch of the island component to re-render it again.
::read-more{to="/docs/guide/going-further/experimental-features#componentislands" icon="i-ph-star-duotone"}
This component is experimental and in order to use it you must enable the `experimental.componentIslands` option in your `nuxt.config`.
::
::note
Global styles of your application are sent with the response.
::

View File

@ -176,6 +176,9 @@ export default defineNuxtModule<ComponentsOptions>({
chunkName: 'components/' + component.kebabName
})
}
if (component.mode === 'server' && !nuxt.options.ssr) {
logger.warn(`Using server components with \`ssr: false\` is not supported with auto-detected component islands. If you need to use server component \`${component.pascalName}\`, set \`experimental.componentIslands\` to \`true\`.`)
}
}
context.components = newComponents
app.components = newComponents

View File

@ -217,7 +217,6 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
'process.env.NUXT_NO_SCRIPTS': !!nuxt.options.features.noScripts && !nuxt.options.dev,
'process.env.NUXT_INLINE_STYLES': !!nuxt.options.features.inlineStyles,
'process.env.NUXT_JSON_PAYLOADS': !!nuxt.options.experimental.renderJsonPayloads,
'process.env.NUXT_COMPONENT_ISLANDS': !!nuxt.options.experimental.componentIslands,
'process.env.NUXT_ASYNC_CONTEXT': !!nuxt.options.experimental.asyncContext,
'process.env.NUXT_SHARED_DATA': !!nuxt.options.experimental.sharedPrerenderData,
'process.dev': nuxt.options.dev,

View File

@ -314,7 +314,7 @@ async function initNuxt (nuxt: Nuxt) {
filePath: resolve(nuxt.options.appDir, 'components/nuxt-island')
})
if (!nuxt.options.ssr) {
if (!nuxt.options.ssr && nuxt.options.experimental.componentIslands !== 'auto') {
nuxt.options.ssr = true
nuxt.options.nitro.routeRules ||= {}
nuxt.options.nitro.routeRules['/**'] = defu(nuxt.options.nitro.routeRules['/**'], { ssr: false })

View File

@ -29,7 +29,7 @@ import unheadPlugins from '#internal/unhead-plugins.mjs'
// eslint-disable-next-line import/no-restricted-paths
import type { NuxtPayload, NuxtSSRContext } from '#app'
// @ts-expect-error virtual file
import { appHead, appRootId, appRootTag, appTeleportId, appTeleportTag } from '#internal/nuxt.config.mjs'
import { appHead, appRootId, appRootTag, appTeleportId, appTeleportTag, componentIslands } from '#internal/nuxt.config.mjs'
// @ts-expect-error virtual file
import { buildAssetsURL, publicAssetsURL } from '#paths'
@ -263,7 +263,7 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
}
// Check for island component rendering
const isRenderingIsland = (process.env.NUXT_COMPONENT_ISLANDS as unknown as boolean && event.path.startsWith('/__nuxt_island'))
const isRenderingIsland = (componentIslands as unknown as boolean && event.path.startsWith('/__nuxt_island'))
const islandContext = isRenderingIsland ? await getIslandContext(event) : undefined
if (import.meta.prerender && islandContext && event.path && await islandCache!.hasItem(event.path)) {
@ -468,7 +468,7 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
bodyAttrs: bodyAttrs ? [bodyAttrs] : [],
bodyPrepend: normalizeChunks([bodyTagsOpen, ssrContext.teleports?.body]),
body: [
process.env.NUXT_COMPONENT_ISLANDS ? replaceIslandTeleports(ssrContext, _rendered.html) : _rendered.html,
componentIslands ? replaceIslandTeleports(ssrContext, _rendered.html) : _rendered.html,
APP_TELEPORT_OPEN_TAG + (HAS_APP_TELEPORTS ? joinTags([ssrContext.teleports?.[`#${appTeleportId}`]]) : '') + APP_TELEPORT_CLOSE_TAG
],
bodyAppend: [bodyTags]

View File

@ -374,10 +374,13 @@ export const nuxtConfigTemplate: NuxtTemplate = {
baseURL: undefined,
headers: undefined
}
const shouldEnableComponentIslands = ctx.nuxt.options.experimental.componentIslands && (
ctx.nuxt.options.dev || ctx.nuxt.options.experimental.componentIslands !== 'auto' || ctx.app.pages?.some(p => p.mode === 'server') || ctx.app.components?.some(c => c.mode === 'server')
)
return [
...Object.entries(ctx.nuxt.options.app).map(([k, v]) => `export const ${camelCase('app-' + k)} = ${JSON.stringify(v)}`),
`export const renderJsonPayloads = ${!!ctx.nuxt.options.experimental.renderJsonPayloads}`,
`export const componentIslands = ${!!ctx.nuxt.options.experimental.componentIslands}`,
`export const componentIslands = ${shouldEnableComponentIslands}`,
`export const payloadExtraction = ${!!ctx.nuxt.options.experimental.payloadExtraction}`,
`export const cookieStore = ${!!ctx.nuxt.options.experimental.cookieStore}`,
`export const appManifest = ${!!ctx.nuxt.options.experimental.appManifest}`,

View File

@ -79,6 +79,10 @@ export default defineNuxtModule({
nuxt.hook('app:templates', async (app) => {
app.pages = await resolvePagesRoutes()
await nuxt.callHook('pages:extend', app.pages)
if (!nuxt.options.ssr && app.pages.some(p => p.mode === 'server')) {
logger.warn('Using server pages with `ssr: false` is not supported with auto-detected component islands. Set `experimental.componentIslands` to `true`.')
}
})
// Restart Nuxt when pages dir is added or removed

View File

@ -172,7 +172,10 @@ export default defineUntypedSchema({
/**
* Experimental component islands support with <NuxtIsland> and .island.vue files.
* @type {true | 'local' | 'local+remote' | Partial<{ remoteIsland: boolean, selectiveClient: boolean }> | false}
*
* By default it is set to 'auto', which means it will be enabled only when there are islands,
* server components or server pages in your app.
* @type {true | 'auto' | 'local' | 'local+remote' | Partial<{ remoteIsland: boolean, selectiveClient: boolean }> | false}
*/
componentIslands: {
$resolve: (val) => {
@ -182,7 +185,7 @@ export default defineUntypedSchema({
if (val === 'local') {
return true
}
return val ?? false
return val ?? 'auto'
}
},