-
Notifications
You must be signed in to change notification settings - Fork 546
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow for multiple render instances #130
Comments
Isn't this already viable?
I think this would create a ton of complexity internally. Why not just create a wrapper component that mounts its own DOM tree using the custom renderer? Sounds cleaner that way.
They are all typed, can you share more details on what is missing? |
If I understand this correctly you're referring to creating a proxy compiler (as
Yes, I was suggesting changing this so you could pass this single instance to the component options, but as adding those component options adds a lot of complexity idea this is not relevant anymore.
In vuejs/core@27913e6#diff-d21cd2abd79b4b986874aa0b370f92b1R9 this last type was added, I hadn't seen that yet, thanks! |
What I mean is something like: import { createRenderer, h, Fragment } from 'vue'
const { render } = createRenderer({ /* your options */ })
const CustomRendererWrapper = {
render: () => h('div', { class: 'custom-renderer' }),
mounted() {
render(h(Fragment, this.$slots.default()), this.$el)
}
} |
We eventually managed to get it to work. The updated hook was also needed to propagate updates to the sub-renderer: import {createRendererForStage} from "./runtime/runtime"
import { h, Fragment } from 'vue'
export const Vugel = {
props: {
settings: {type: Object, default: {w: 600, h: 600}}
},
render: () => {
return h('canvas', { class: 'custom-renderer' })
},
mounted() {
const stageOptions = {...(this as any).settings};
const stage = new lng.Stage({ ...stageOptions, canvas: (this as any).$el });
const render = createRendererForStage(stage);
(this as any).$vugelRender = render;
render(h(Fragment, (this as any).$slots.default()), render.stageRoot)
},
updated() {
const render = (this as any).$vugelRender;
render(h(Fragment, (this as any).$slots.default()), render.stageRoot)
}
} We still have an issue with template compilation. Example of a HTML template that spawns a webgl-rendered component: <script type="text/x-template" id="demo-template">
<div :scale="scale"></div>
<vugel :settings="{w: 900, h: 900}" v-on:click="click">
<gl :prop="scale"></gl>
</vugel>
<vugel :settings="{w: 900, h: 900}" v-on:click="click">
<gl :prop="scale * 0.5"></gl>
</vugel>
</script> Notice that 'gl' is a sub component. It's template consists out of elements for the webgl render tree and looks like this: <script type="text/x-template" id="gl-template">
<node :scale="prop">
<node>
<rect v-for="(item,index) in images" :x="item.x" :w="item.w" :y="index * 40" :h="20" :color="0xff00ffff">
<gl-image :x="index * 100"></gl-image>
</rect>
</node>
</node>
</script> gl-image is a sub component, while all other nodes such as node, rect are native elements in the webgl render tree We can't add webgl tree nodes as children (slots) of the 'vugel' element directly because the compiler would use the HTML isNativeTag implementation, so it would mistakingly think that these are components. For the Vugel slots, this is not a problem as we can use a wrapping component in practice. Unfortunately, a single compiler is registered using registerRuntimeCompiler, and used for all templates including our webgl components. Our workaround requires us to register a wrapping compiler and then decide on whether to use the compiler-vugel or compiler-core based on the properties of the template: import {
compile,
registerRuntimeCompiler,
RenderFunction
} from 'vue'
import { CompilerOptions } from '@vue/compiler-core'
import compileVugel from "./compiler/compileToFunction"
// Overwrite Vue compiler with our wrapped compiler.
function compileToFunction(
template: string | HTMLElement,
options?: CompilerOptions
): RenderFunction {
if (typeof template === "string") {
if (template.startsWith("#gl-")) {
return compileVugel(template, options)
}
}
return compile(template, options)
}
registerRuntimeCompiler(compileToFunction) We don't like to replace the runtime compiler as it seems like bad practice. Do you also have a suggestion for this @yyx990803? |
Responding on my own question: const Demo = {
components: {
DemoList
},
data: () => {
return {rot: 0}
},
render: compileVugel('#gl-demo')
} Instead of using the template directive we can apply the compile function and set the render function directly (which is the same as what happens when specifying a template). |
A working version which also updates the nested components properly for the interested people: import {Fragment, h} from 'vue'
import {effect, ref} from "@vue/reactivity";
import {defineComponent} from "@vue/runtime-core";
type VugelPropType = {
settings: {
w: number,
h: number
}
};
export const Vugel = defineComponent({
props: {
settings: {type: Object, default: {w: 600, h: 600}}
},
setup(props: VugelPropType, setupContext) {
const elRef: Ref<HTMLElement> = ref();
onMounted(() => {
let rendered = false;
let vugelRenderer: VugelRender;
effect(() => {
if (!rendered) {
rendered = true;
const stageOptions = {...props.settings};
const stage = new lng.Stage({...stageOptions, canvas: elRef.value});
vugelRenderer = createRendererForStage(stage);
}
vugelRenderer(h(Fragment, setupContext.slots.default()), vugelRenderer.stageRoot)
});
});
return () => h('canvas', {class: 'custom-renderer', ref: elRef})
}
}); where |
I have some ideas about the following but I'd like to hear some comments first, before making it an official RFC:
Context
Vue3 has support for a custom renderer, which we have been trying out. This custom renderer uses WebGL to render, using Vue templates / components. This is a great feature, and it was relatively easy to accomplish.
Request
Right now, it is only possible to register 1 renderer for the entire Vue instance. This means you can't use multiple instances of the same renderer (multi canvases for WebGL) and you can't mix multiple renderers (e.g. DOM & WebGL). It would be really nice if this was possible, to for example allow for easy state sharing between the two renderers.
My proposal
I would propose the following changes to be made:
ComponentOptions
; the renderer and the compiler the component will usenodeOps
and all other required objects to create a renderer & compiler and provide more context about the renderer & compiler instance being used, so make it easier to create a custom renderer & compiler.Some other remarks:
DOM > some other renderer 1 > some other renderer 2
. This is because I would have no clueDOM > some other renderer > DOM
, if you have suggestions I'd like to hear them!DOM > DOM > GL > GL > yet another renderer
, I would only need to annotate the fact that I want to useGL
as renderer on the first one of the two usages, if that makes sense.If something is vague or if you have other remarks I'd love to hear them!
The text was updated successfully, but these errors were encountered: