From fdd38f958cdec8d3de06bb3ce5f3af779d3fe493 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Fri, 1 Apr 2022 15:06:48 +0100 Subject: [PATCH] feat(nitro): support for rendering ssr teleports to body (#3909) Co-authored-by: pooya parsa --- .eslintrc | 1 + docs/content/3.docs/1.usage/10.teleports.md | 34 ++++++++++++++++++++ docs/content/4.examples/1.app/teleport.md | 15 +++++++++ examples/app/teleport/app.vue | 22 +++++++++++++ examples/app/teleport/components/MyModal.vue | 34 ++++++++++++++++++++ examples/app/teleport/nuxt.config.ts | 7 ++++ examples/app/teleport/package.json | 13 ++++++++ examples/app/teleport/tsconfig.json | 3 ++ packages/nitro/src/runtime/app/render.ts | 3 +- packages/nuxt3/src/core/templates.ts | 2 +- yarn.lock | 9 ++++++ 11 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 docs/content/3.docs/1.usage/10.teleports.md create mode 100644 docs/content/4.examples/1.app/teleport.md create mode 100644 examples/app/teleport/app.vue create mode 100644 examples/app/teleport/components/MyModal.vue create mode 100644 examples/app/teleport/nuxt.config.ts create mode 100644 examples/app/teleport/package.json create mode 100644 examples/app/teleport/tsconfig.json diff --git a/.eslintrc b/.eslintrc index 1f1318423f..2e2b3cfa1a 100644 --- a/.eslintrc +++ b/.eslintrc @@ -16,6 +16,7 @@ "vue/multi-word-component-names": "off", "vue/one-component-per-file": "off", "vue/require-default-prop": "off", + "vue/no-multiple-template-root": "off", "jsdoc/require-jsdoc": "off", "jsdoc/require-param": "off", "jsdoc/require-returns": "off", diff --git a/docs/content/3.docs/1.usage/10.teleports.md b/docs/content/3.docs/1.usage/10.teleports.md new file mode 100644 index 0000000000..7860b9a985 --- /dev/null +++ b/docs/content/3.docs/1.usage/10.teleports.md @@ -0,0 +1,34 @@ +# Teleports + +Vue 3 provides the [`` 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 `` 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 `` wrapper. + +## Example: body teleport + +```vue + +``` + +## Example: client-side teleport + +```vue + + + + + + +``` diff --git a/docs/content/4.examples/1.app/teleport.md b/docs/content/4.examples/1.app/teleport.md new file mode 100644 index 0000000000..9efd5d149d --- /dev/null +++ b/docs/content/4.examples/1.app/teleport.md @@ -0,0 +1,15 @@ +--- +template: Example +--- + +# Teleport + +Vue 3 provides the [`` 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 `` 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"} diff --git a/examples/app/teleport/app.vue b/examples/app/teleport/app.vue new file mode 100644 index 0000000000..bc3f9b9966 --- /dev/null +++ b/examples/app/teleport/app.vue @@ -0,0 +1,22 @@ + diff --git a/examples/app/teleport/components/MyModal.vue b/examples/app/teleport/components/MyModal.vue new file mode 100644 index 0000000000..804c2b18f0 --- /dev/null +++ b/examples/app/teleport/components/MyModal.vue @@ -0,0 +1,34 @@ + + + + + diff --git a/examples/app/teleport/nuxt.config.ts b/examples/app/teleport/nuxt.config.ts new file mode 100644 index 0000000000..9850816d15 --- /dev/null +++ b/examples/app/teleport/nuxt.config.ts @@ -0,0 +1,7 @@ +import { defineNuxtConfig } from 'nuxt3' + +export default defineNuxtConfig({ + modules: [ + '@nuxt/ui' + ] +}) diff --git a/examples/app/teleport/package.json b/examples/app/teleport/package.json new file mode 100644 index 0000000000..0e7a8c4f2e --- /dev/null +++ b/examples/app/teleport/package.json @@ -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" + } +} diff --git a/examples/app/teleport/tsconfig.json b/examples/app/teleport/tsconfig.json new file mode 100644 index 0000000000..4b34df1571 --- /dev/null +++ b/examples/app/teleport/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./.nuxt/tsconfig.json" +} diff --git a/packages/nitro/src/runtime/app/render.ts b/packages/nitro/src/runtime/app/render.ts index d26a5045d0..e854e983d4 100644 --- a/packages/nitro/src/runtime/app/render.ts +++ b/packages/nitro/src/runtime/app/render.ts @@ -158,6 +158,7 @@ async function renderHTML (payload, rendered, ssrContext) { HEAD: headTags + rendered.renderResourceHints() + rendered.renderStyles() + (ssrContext.styles || ''), BODY_ATTRS: bodyAttrs, + BODY_PREPEND: ssrContext.teleports?.body || '', APP: bodyScriptsPrepend + html + state + rendered.renderScripts() + bodyScripts }) } @@ -171,7 +172,7 @@ function _interopDefault (e) { } function cachedImport (importer: () => Promise) { - return cachedResult(() => importer().then(_interopDefault)) + return cachedResult(() => importer().then(_interopDefault)) as () => Promise } function cachedResult (fn: () => Promise): () => Promise { diff --git a/packages/nuxt3/src/core/templates.ts b/packages/nuxt3/src/core/templates.ts index 5ff36cc59a..99995fa393 100644 --- a/packages/nuxt3/src/core/templates.ts +++ b/packages/nuxt3/src/core/templates.ts @@ -80,7 +80,7 @@ export const appViewTemplate = { {{ HEAD }} - +{{ BODY_PREPEND }} {{ APP }} diff --git a/yarn.lock b/yarn.lock index 3fbb4c7a15..6a23d8f1d3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10592,6 +10592,15 @@ __metadata: languageName: unknown 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": version: 0.0.0-use.local resolution: "example-test@workspace:examples/advanced/test"