How to use Render Functions with Vue 3 Composition API
Render Functions & JSX
Vue recommends using templates to build applications in the vast majority of cases. However, there are situations where we need the full programmatic power of JavaScript. That’s where we can use the render function.
If you are new to the concept of virtual DOM and render functions, make sure to read the Rendering Mechanism chapter first.
What is Composition API?
Composition API is a set of APIs that allows us to author Vue components using imported functions instead of declaring options. It is an umbrella term that covers the following APIs:
- Reactivity API, e.g.
ref()
andreactive()
, that allows us to directly create a reactive state, computed state, and watchers. - Lifecycle Hooks, e.g.
onMounted()
andonUnmounted()
, that allows us to programmatically hook into the component lifecycle. - Dependency Injection, i.e.
provide()
andinject()
, that allows us to leverage Vue’s dependency injection system while using Reactivity APIs.
Composition API is a built-in feature of Vue 3, and is currently available to Vue 2 via the officially maintained @vue/composition-api plugin. In Vue 3, it is also primarily used together with the <script setup>
syntax in Single-File Components. Here’s a basic example of a component using Composition API:
<script setup>
import { ref, onMounted } from 'vue'
// reactive state
const count = ref(0)
// functions that mutate state and trigger updates
function increment() {
count.value++
}
// lifecycle hooks
onMounted(() => {
console.log(`The initial count is ${count.value}.`)
})
</script>
<template>
<button @click="increment">Count is: {{ count }}</button>
</template>
Despite an API style based on function composition, Composition API is NOT functional programming. Composition API is based on Vue’s mutable, fine-grained reactivity paradigm, whereas functional programming emphasizes immutability.
If you are interested in learning how to use Vue with Composition API, you can set the site-wide API preference to Composition API using the toggle at the top of the left sidebar, and then go through the guide from the beginning.
Read full documentation https://vuejs.org/
Let’s create a scenario. Back in Vue 2, I used to call render()
like this:
export default {
mounted(){
...
}, render(){
...
}, methods(){
...
}
}
I’m now trying to do the same with Vue 3 and the composition API. Here is what I tried:
<script>
export default {
...
setup(props, context) {
...
const create_canvas = (h, id, props) => {
_id.value = id
_attrs.value = props.attrs
return () => h('div', {
class: `trading-vue-${id}`,
style: {
left: props.position.x + 'px',
top: props.position.y + 'px',
position: 'absolute',
}
}, [
h('canvas', Object.assign({
id: `${props.tv_id}-${id}-canvas`,
onmousemove: e => renderer.mousemove(e),
onmouseout: e => renderer.mouseout(e),
onmouseup: e => renderer.mouseup(e),
onmousedown: e => renderer.mousedown(e),
ref: 'canvas',
style: props.style,
}, props.attrs))
].concat(props.hs || []))
};
function render() {
const id = props.grid_id
const layout = props.layout.grids[id]
return () => create_canvas(h, `grid-${id}`, {
position: {
x: 0,
y: layout.offset || 0
},
attrs: {
width: layout.width,
height: layout.height,
overflow: 'hidden'
},
style: {
backgroundColor: props.colors.back
},
hs: [
h(Crosshair, Object.assign(
common_props(),
layer_events
)),
h(KeyboardListener, keyboard_events),
h(UxLayer, {
id,
tv_id: props.tv_id,
uxs: uxs.value,
colors: props.colors,
config: props.config,
updater: Math.random(),
onCustomEvent: emit_ux_event
})
].concat(get_overlays(h))
})
};
render()
}
}
</script>
This doesn’t seem to return anything in my template. I think that I’m not calling the render function in the right way. Can anyone help me understand how to use it?
As per my understanding, h() is a short form to create the vnodes and accept 3 parameters.
h( tag name, props/attributes, array of children)
As per my understanding, In create_canvas you are trying to make a div which contains a class and inline styles as a props/attributes and we are creating a canvas as children of this div vnode. Hence, Instead of returning the vNode directly from setup(), it should return a render function that returns a vNode.
<script>
export default {
props: {
// props will come here
},
setup(props) {
// render() { h(...) } ❌
return () => {
h('div', {
class: `trading-vue-${id}`,
style: {
left: props.position.x + 'px',
top: props.position.y + 'px',
position: 'absolute',
}
}, [
h('canvas', Object.assign({
id: `${props.tv_id}-${id}-canvas`,
onmousemove: e => renderer.mousemove(e),
onmouseout: e => renderer.mouseout(e),
onmouseup: e => renderer.mouseup(e),
onmousedown: e => renderer.mousedown(e),
ref: 'canvas',
style: props.style,
}, props.attrs))
].concat(props.hs || []))
} ✅
}
}
</script>
This might help to call render functions in vue 3 composition API(s).