mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-29 00:52:01 +00:00
feat(nitro): support for rendering ssr teleports to body (#3909)
Co-authored-by: pooya parsa <pyapar@gmail.com>
This commit is contained in:
parent
ade3378a00
commit
fdd38f958c
@ -16,6 +16,7 @@
|
|||||||
"vue/multi-word-component-names": "off",
|
"vue/multi-word-component-names": "off",
|
||||||
"vue/one-component-per-file": "off",
|
"vue/one-component-per-file": "off",
|
||||||
"vue/require-default-prop": "off",
|
"vue/require-default-prop": "off",
|
||||||
|
"vue/no-multiple-template-root": "off",
|
||||||
"jsdoc/require-jsdoc": "off",
|
"jsdoc/require-jsdoc": "off",
|
||||||
"jsdoc/require-param": "off",
|
"jsdoc/require-param": "off",
|
||||||
"jsdoc/require-returns": "off",
|
"jsdoc/require-returns": "off",
|
||||||
|
34
docs/content/3.docs/1.usage/10.teleports.md
Normal file
34
docs/content/3.docs/1.usage/10.teleports.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# Teleports
|
||||||
|
|
||||||
|
Vue 3 provides the [`<Teleport>` component](https://vuejs.org/guide/built-ins/teleport.html) which allows content to be rendered elsewhere in the DOM, outside of the Vue application.
|
||||||
|
|
||||||
|
The `to` target of `<Teleport>` expects a CSS selector string or an actual DOM node. Nuxt currently has SSR support for teleports to `body` only, with client-side support for other targets using a `<ClientOnly>` wrapper.
|
||||||
|
|
||||||
|
## Example: body teleport
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<button @click="open = true">
|
||||||
|
Open Modal
|
||||||
|
</button>
|
||||||
|
<Teleport to="body">
|
||||||
|
<div v-if="open" class="modal">
|
||||||
|
<p>Hello from the modal!</p>
|
||||||
|
<button @click="open = false">
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example: client-side teleport
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<ClientOnly>
|
||||||
|
<Teleport to="#some-selector">
|
||||||
|
<!-- content -->
|
||||||
|
</Teleport>
|
||||||
|
</ClientOnly>
|
||||||
|
</template>
|
||||||
|
```
|
15
docs/content/4.examples/1.app/teleport.md
Normal file
15
docs/content/4.examples/1.app/teleport.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
template: Example
|
||||||
|
---
|
||||||
|
|
||||||
|
# Teleport
|
||||||
|
|
||||||
|
Vue 3 provides the [`<Teleport>` component](https://vuejs.org/guide/built-ins/teleport.html) which allows content to be rendered elsewhere in the DOM, outside of the Vue application.
|
||||||
|
|
||||||
|
This example shows how to use the `<Teleport>` with client-side and server-side rendering.
|
||||||
|
|
||||||
|
::alert{type=info icon=👉}
|
||||||
|
Learn more about [teleports](/docs/usage/teleports).
|
||||||
|
::
|
||||||
|
|
||||||
|
::sandbox{repo="nuxt/framework" branch="main" dir="examples/app/teleport" file="app.vue"}
|
22
examples/app/teleport/app.vue
Normal file
22
examples/app/teleport/app.vue
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<template>
|
||||||
|
<NuxtExampleLayout example="app/teleport">
|
||||||
|
<div>
|
||||||
|
<!-- SSR Teleport -->
|
||||||
|
<Teleport to="body">
|
||||||
|
SSR Teleport
|
||||||
|
</Teleport>
|
||||||
|
|
||||||
|
<!-- Client Teleport -->
|
||||||
|
<ClientOnly>
|
||||||
|
<Teleport to="body">
|
||||||
|
<div>
|
||||||
|
Hello from a client-side teleport!
|
||||||
|
</div>
|
||||||
|
</Teleport>
|
||||||
|
</ClientOnly>
|
||||||
|
|
||||||
|
<!-- Modal Example -->
|
||||||
|
<MyModal />
|
||||||
|
</div>
|
||||||
|
</NuxtExampleLayout>
|
||||||
|
</template>
|
34
examples/app/teleport/components/MyModal.vue
Normal file
34
examples/app/teleport/components/MyModal.vue
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
open: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NButton @click="open = true">
|
||||||
|
Open Modal
|
||||||
|
</NButton>
|
||||||
|
<Teleport to="body">
|
||||||
|
<NCard v-if="open" class="modal p4">
|
||||||
|
<p>Hello from the modal!</p>
|
||||||
|
<NButton @click="open = false">
|
||||||
|
Close
|
||||||
|
</NButton>
|
||||||
|
</NCard>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.modal {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 999;
|
||||||
|
top: 20%;
|
||||||
|
left: 50%;
|
||||||
|
width: 300px;
|
||||||
|
margin-left: -150px;
|
||||||
|
}
|
||||||
|
</style>
|
7
examples/app/teleport/nuxt.config.ts
Normal file
7
examples/app/teleport/nuxt.config.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { defineNuxtConfig } from 'nuxt3'
|
||||||
|
|
||||||
|
export default defineNuxtConfig({
|
||||||
|
modules: [
|
||||||
|
'@nuxt/ui'
|
||||||
|
]
|
||||||
|
})
|
13
examples/app/teleport/package.json
Normal file
13
examples/app/teleport/package.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "example-teleport",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"build": "nuxi build",
|
||||||
|
"dev": "nuxi dev",
|
||||||
|
"start": "nuxi preview"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@nuxt/ui": "npm:@nuxt/ui-edge@latest",
|
||||||
|
"nuxt3": "latest"
|
||||||
|
}
|
||||||
|
}
|
3
examples/app/teleport/tsconfig.json
Normal file
3
examples/app/teleport/tsconfig.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"extends": "./.nuxt/tsconfig.json"
|
||||||
|
}
|
@ -158,6 +158,7 @@ async function renderHTML (payload, rendered, ssrContext) {
|
|||||||
HEAD: headTags +
|
HEAD: headTags +
|
||||||
rendered.renderResourceHints() + rendered.renderStyles() + (ssrContext.styles || ''),
|
rendered.renderResourceHints() + rendered.renderStyles() + (ssrContext.styles || ''),
|
||||||
BODY_ATTRS: bodyAttrs,
|
BODY_ATTRS: bodyAttrs,
|
||||||
|
BODY_PREPEND: ssrContext.teleports?.body || '',
|
||||||
APP: bodyScriptsPrepend + html + state + rendered.renderScripts() + bodyScripts
|
APP: bodyScriptsPrepend + html + state + rendered.renderScripts() + bodyScripts
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -171,7 +172,7 @@ function _interopDefault (e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function cachedImport <M> (importer: () => Promise<M>) {
|
function cachedImport <M> (importer: () => Promise<M>) {
|
||||||
return cachedResult(() => importer().then(_interopDefault))
|
return cachedResult(() => importer().then(_interopDefault)) as () => Promise<M>
|
||||||
}
|
}
|
||||||
|
|
||||||
function cachedResult <T> (fn: () => Promise<T>): () => Promise<T> {
|
function cachedResult <T> (fn: () => Promise<T>): () => Promise<T> {
|
||||||
|
@ -80,7 +80,7 @@ export const appViewTemplate = {
|
|||||||
{{ HEAD }}
|
{{ HEAD }}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body {{ BODY_ATTRS }}>
|
<body {{ BODY_ATTRS }}>{{ BODY_PREPEND }}
|
||||||
{{ APP }}
|
{{ APP }}
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
@ -10592,6 +10592,15 @@ __metadata:
|
|||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
|
"example-teleport@workspace:examples/app/teleport":
|
||||||
|
version: 0.0.0-use.local
|
||||||
|
resolution: "example-teleport@workspace:examples/app/teleport"
|
||||||
|
dependencies:
|
||||||
|
"@nuxt/ui": "npm:@nuxt/ui-edge@latest"
|
||||||
|
nuxt3: latest
|
||||||
|
languageName: unknown
|
||||||
|
linkType: soft
|
||||||
|
|
||||||
"example-test@workspace:examples/advanced/test":
|
"example-test@workspace:examples/advanced/test":
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "example-test@workspace:examples/advanced/test"
|
resolution: "example-test@workspace:examples/advanced/test"
|
||||||
|
Loading…
Reference in New Issue
Block a user