diff --git a/active-rfcs/0025-teleport.md b/active-rfcs/0025-teleport.md new file mode 100644 index 00000000..5ae8226b --- /dev/null +++ b/active-rfcs/0025-teleport.md @@ -0,0 +1,321 @@ +- Start Date: 2020-01-20 +- Target Major Version: (3.x) +- Reference Issues: (fill in existing related issues, if any) +- Implementation PR: + +# Summary + +- Adds a `` component to Vue core +- the component requires a target element, provided through a prop which expects an `HTMLElement` or a `querySelector` string. +- the component moves its children to the element identified by the DOM selector +- At the virtual DOM level, the children stay descendants of the `` though, so they i.e. have access to injections from its ancestors + +# Basic example + +```html + +
+

Move the #content with the portal component

+ +
+

+ this will be moved to #endofbody.
+ Pretend that it's a modal +

+ +
+
+
+
+ + +``` + +This will result in the following behaviour: + +1. All of the children of `` - in this example: `
` and `` - will be appended to `
` +2. the `` component as one of these children will remain a child component of the ``'s parent (the `` is transparent). + +```html +
+ +
+
+
+

+ this will be moved to #endofbody.
+ Pretend that it's a modal +

+
Placeholder
+
+
+``` + +# Motivation + +Vue encourages us to build our UIs by encapsulating UI and related behaviour into components, which we can nest inside one another to build a tree of components that make up your application UI. That model has proven itself in Vue and other frameworks in many ways, but there's one weakness this RFC seeks to address: + +Sometimes, a part of a component's template belongs into this component _logically_, while from a technical point of view (i.e.: styling requirements), it would be preferable to move this part of the template somewhere else in the DOM, breaking it out of it's deeply nested position without our DOM tree. + +## Use cases + +### z-Index + +The main use cases for such a behaviour are usually styling-related. Various common UI patterns such as modals, dialogs, dropdown menus, notifications etc. require fixed or absolute positioning and management of their z-index. + +In order to work around issues with [z-index Stacking Context](https://philipwalton.com/articles/what-no-one-told-you-about-z-index/) behaviour, it's a common pattern to put the DOM elements of those components right before the `` tag in order to move them out of any parent element's z-index stacking context. + +### Widgets + +Many apps have the concept of widgets, where their UI has an outlet (i.e. in a sidebar or dashboard) where other parts of the application, i.e. plugins, can inject small pieces of UI. + +In Single Page Applications, where our Javascript controls essentially the whole page, this is generally not a challenge. But in situations where our Vue app only controls a part of the page, it currently proves to be challenging (but impossible) to mount individual elements and components in other parts of the page. + +With ``, we have a straightforward way to mount child components to other locations in the DOM declaratively. + +# Detailed design + +## Implementation as an internal component + +The `` "component" is an internal component like `` and ``. It's tree-shakable, so if you don't use the feature, the component code will be dropped from the final bundle. + +### Usage in templates + +In templates, the compiler will add the import for the `` component to the generated code, so it can be used just like this: + +```js +export default { + template: ` +
+ + Some content. + +
+ ` +}; +``` + +When using a render function or JSX, component has to imported first, like any other component: + +```js +import { Teleport, h } from "vue"; +export default { + render() { + return h("div", [h(Teleport, { to: "#endofbody" }, ["Some content"])]); + }, + // or with JSX: + render() { +
+ Some content +
; + } +}; +``` + +## using multiple portals on the same target + +A common use case scenario would be a reusable `` component of which there might be multiple instances active at the same time. For this kind of scenario, multiple `` components can mount their content to the same target element. The order will be a simple `append` - later mounts will be located after earlier ones within the target element. + +```html + +
A
+
+ +
B
+
+ + +
+
A
+
B
+
+``` + +In the discussions for this RFC so far, more complex behaviour (optional prepending, defining the order ...) was discussed, but concerns about complexity and foreseeable issues with SSR and hydration lead us to limit this behaviour to a simple `append`. + +## Props + +### `to` + +The component has only one _required_ prop, named `to`. It accepts a string wich has to be a valid query selector, or an HTMLElement (if used in a browser environment). + +```html + + + + + + + + +> +``` + +### `disabled` + +This optional prop can be used to disable the portal's functionality, which means that its slot content will not be moved anywhere and instead be rendered where you specified the `` in the surrounding parent component. + +```html + + +``` + +Changing its value dynamically allows to move the same DOM elements between the target specified by the `to` prop, and the actual location in the surrounding parent component. This means that any components inside of the `` will be kept alive and keep their internal state. Likewise, a `