vuejs / rfcs

RFCs for substantial changes / feature additions to Vue core
4.85k stars 549 forks source link

Teleport as a composable/function #552

Closed segevfiner closed 10 months ago

segevfiner commented 11 months ago

What problem does this feature solve?

A scenario I have encountered multiple times by now is needing to pass a Vue component down through a third party library that also does it's own DOM handling. For example Tippy.js or Tiptap/ProseMirror. This proves to be a bit more involved in some scenarios as the libraries require passing down a DOM element, and/or doing so outside the context of a Vue component, e.g. Some class/object that is created internally from some Vue component.

Having some API that can be used to handle this case elegantly without needing to create multiple Vue apps (Which then have separate context/globals without undocumented hacks), or having to wire though a teleport in some upper component by yourself can help make this simpler and improve the experience in the devtools hopefully.

The trick that is used by TipTap now is essentially collecting the requested components along wiith a JS created detached DOM element, passing them along to Teleport in an upper level component and then manually connecting the resulting detached DOM to where you need it. But this is quite involved. This is done by this class https://github.com/ueberdosis/tiptap/blob/develop/packages/vue-3/src/VueRenderer.ts and other code in that package. A standalone example of it can be seen here https://stackblitz.com/edit/vitejs-vite-qck2xd?file=src%2Fcomponents%2FTeleportSample.vue.

vue-tippy had the same issue when wanting to implement its useTippy composable, it initially used clever trickery with vnodes, getCurrentInstance and so forth but that is undocumented and didn't work with the devtools, it currently switch to just creating additional Vue apps and wiring the context from the original Vue app in an undocumented way. See https://github.com/KABBOUCHI/vue-tippy/issues/290.

cc @KABBOUCHI

What does the proposed API look like?

What if we had an API like:

const { dom } = useTeleport(Component);

// Or for cases where the app context might not be obvious, e.g. Some event handler, inner life-cycle of Tiptap/ProseMirror?
const { dom } = useTeleport(Component, { app });

This should allow using a teleport from the current app without having to wire it yourself. Of course, how this integrates with the devtools, which app, and so forth needs careful consideration and thought.