mirror of
https://github.com/nuxt/nuxt.git
synced 2025-02-19 15:10:58 +00:00
feat(nuxt): enable islands if server pages/components present (#26223)
This commit is contained in:
parent
f080c426a2
commit
9bfd988ca6
@ -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.
|
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
|
## 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.
|
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.
|
||||||
|
@ -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.
|
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
|
::note
|
||||||
Global styles of your application are sent with the response.
|
Global styles of your application are sent with the response.
|
||||||
::
|
::
|
||||||
|
@ -176,6 +176,9 @@ export default defineNuxtModule<ComponentsOptions>({
|
|||||||
chunkName: 'components/' + component.kebabName
|
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
|
context.components = newComponents
|
||||||
app.components = newComponents
|
app.components = newComponents
|
||||||
|
@ -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_NO_SCRIPTS': !!nuxt.options.features.noScripts && !nuxt.options.dev,
|
||||||
'process.env.NUXT_INLINE_STYLES': !!nuxt.options.features.inlineStyles,
|
'process.env.NUXT_INLINE_STYLES': !!nuxt.options.features.inlineStyles,
|
||||||
'process.env.NUXT_JSON_PAYLOADS': !!nuxt.options.experimental.renderJsonPayloads,
|
'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_ASYNC_CONTEXT': !!nuxt.options.experimental.asyncContext,
|
||||||
'process.env.NUXT_SHARED_DATA': !!nuxt.options.experimental.sharedPrerenderData,
|
'process.env.NUXT_SHARED_DATA': !!nuxt.options.experimental.sharedPrerenderData,
|
||||||
'process.dev': nuxt.options.dev,
|
'process.dev': nuxt.options.dev,
|
||||||
|
@ -314,7 +314,7 @@ async function initNuxt (nuxt: Nuxt) {
|
|||||||
filePath: resolve(nuxt.options.appDir, 'components/nuxt-island')
|
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.ssr = true
|
||||||
nuxt.options.nitro.routeRules ||= {}
|
nuxt.options.nitro.routeRules ||= {}
|
||||||
nuxt.options.nitro.routeRules['/**'] = defu(nuxt.options.nitro.routeRules['/**'], { ssr: false })
|
nuxt.options.nitro.routeRules['/**'] = defu(nuxt.options.nitro.routeRules['/**'], { ssr: false })
|
||||||
|
@ -29,7 +29,7 @@ import unheadPlugins from '#internal/unhead-plugins.mjs'
|
|||||||
// eslint-disable-next-line import/no-restricted-paths
|
// eslint-disable-next-line import/no-restricted-paths
|
||||||
import type { NuxtPayload, NuxtSSRContext } from '#app'
|
import type { NuxtPayload, NuxtSSRContext } from '#app'
|
||||||
// @ts-expect-error virtual file
|
// @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
|
// @ts-expect-error virtual file
|
||||||
import { buildAssetsURL, publicAssetsURL } from '#paths'
|
import { buildAssetsURL, publicAssetsURL } from '#paths'
|
||||||
|
|
||||||
@ -263,7 +263,7 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for island component rendering
|
// 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
|
const islandContext = isRenderingIsland ? await getIslandContext(event) : undefined
|
||||||
|
|
||||||
if (import.meta.prerender && islandContext && event.path && await islandCache!.hasItem(event.path)) {
|
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] : [],
|
bodyAttrs: bodyAttrs ? [bodyAttrs] : [],
|
||||||
bodyPrepend: normalizeChunks([bodyTagsOpen, ssrContext.teleports?.body]),
|
bodyPrepend: normalizeChunks([bodyTagsOpen, ssrContext.teleports?.body]),
|
||||||
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
|
APP_TELEPORT_OPEN_TAG + (HAS_APP_TELEPORTS ? joinTags([ssrContext.teleports?.[`#${appTeleportId}`]]) : '') + APP_TELEPORT_CLOSE_TAG
|
||||||
],
|
],
|
||||||
bodyAppend: [bodyTags]
|
bodyAppend: [bodyTags]
|
||||||
|
@ -374,10 +374,13 @@ export const nuxtConfigTemplate: NuxtTemplate = {
|
|||||||
baseURL: undefined,
|
baseURL: undefined,
|
||||||
headers: 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 [
|
return [
|
||||||
...Object.entries(ctx.nuxt.options.app).map(([k, v]) => `export const ${camelCase('app-' + k)} = ${JSON.stringify(v)}`),
|
...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 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 payloadExtraction = ${!!ctx.nuxt.options.experimental.payloadExtraction}`,
|
||||||
`export const cookieStore = ${!!ctx.nuxt.options.experimental.cookieStore}`,
|
`export const cookieStore = ${!!ctx.nuxt.options.experimental.cookieStore}`,
|
||||||
`export const appManifest = ${!!ctx.nuxt.options.experimental.appManifest}`,
|
`export const appManifest = ${!!ctx.nuxt.options.experimental.appManifest}`,
|
||||||
|
@ -79,6 +79,10 @@ export default defineNuxtModule({
|
|||||||
nuxt.hook('app:templates', async (app) => {
|
nuxt.hook('app:templates', async (app) => {
|
||||||
app.pages = await resolvePagesRoutes()
|
app.pages = await resolvePagesRoutes()
|
||||||
await nuxt.callHook('pages:extend', app.pages)
|
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
|
// Restart Nuxt when pages dir is added or removed
|
||||||
|
@ -172,7 +172,10 @@ export default defineUntypedSchema({
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Experimental component islands support with <NuxtIsland> and .island.vue files.
|
* 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: {
|
componentIslands: {
|
||||||
$resolve: (val) => {
|
$resolve: (val) => {
|
||||||
@ -182,7 +185,7 @@ export default defineUntypedSchema({
|
|||||||
if (val === 'local') {
|
if (val === 'local') {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return val ?? false
|
return val ?? 'auto'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user