3.8 KiB
3.8 KiB
title, impact, impactDescription, type, tags
| title | impact | impactDescription | type | tags | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| Render Function Patterns and Performance | MEDIUM | Render functions require explicit patterns for lists, events, v-model, and performance to stay correct and maintainable | best-practice |
|
Render Function Patterns and Performance
Impact: MEDIUM - Render functions are powerful but opt out of template compiler optimizations. Use them intentionally and apply the key patterns below to keep output correct and performant.
Task List
- Prefer templates; use render functions only when templates cannot express the logic
- Always add stable keys when rendering lists with
h()/JSX - Use
withModifiers/withKeysfor event modifiers - Implement
v-modelviamodelValue+onUpdate:modelValue - Apply custom directives with
withDirectives - Use functional components for stateless presentational UI
Prefer templates over render functions
BAD:
<script setup>
import { h, ref } from 'vue'
const count = ref(0)
const render = () => h('div', `Count: ${count.value}`)
</script>
GOOD:
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<div>Count: {{ count }}</div>
</template>
Always add keys for list rendering
BAD:
import { h, ref } from 'vue'
export default {
setup() {
const items = ref([{ id: 1, name: 'Apple' }])
return () => h('ul',
items.value.map(item => h('li', item.name))
)
}
}
GOOD:
import { h, ref } from 'vue'
export default {
setup() {
const items = ref([{ id: 1, name: 'Apple' }])
return () => h('ul',
items.value.map(item => h('li', { key: item.id }, item.name))
)
}
}
Use withModifiers / withKeys for event modifiers
BAD:
import { h } from 'vue'
export default {
setup() {
const handleClick = (e) => {
e.stopPropagation()
e.preventDefault()
}
return () => h('button', { onClick: handleClick }, 'Click')
}
}
GOOD:
import { h, withModifiers, withKeys } from 'vue'
export default {
setup() {
const handleClick = () => {}
const handleEnter = () => {}
return () => h('div', [
h('button', {
onClick: withModifiers(handleClick, ['stop', 'prevent'])
}, 'Click'),
h('input', {
onKeyup: withKeys(handleEnter, ['enter'])
})
])
}
}
Implement v-model explicitly
BAD:
import { h, ref } from 'vue'
import CustomInput from './CustomInput.vue'
export default {
setup() {
const text = ref('')
return () => h(CustomInput, { modelValue: text.value })
}
}
GOOD:
import { h, ref } from 'vue'
import CustomInput from './CustomInput.vue'
export default {
setup() {
const text = ref('')
return () => h(CustomInput, {
modelValue: text.value,
'onUpdate:modelValue': (value) => { text.value = value }
})
}
}
Use withDirectives for custom directives
BAD:
import { h } from 'vue'
const vFocus = { mounted: (el) => el.focus() }
export default {
setup() {
return () => h('input', { 'v-focus': true })
}
}
GOOD:
import { h, withDirectives } from 'vue'
const vFocus = { mounted: (el) => el.focus() }
export default {
setup() {
return () => withDirectives(h('input'), [[vFocus]])
}
}
Prefer functional components for stateless UI
BAD:
import { h } from 'vue'
export default {
setup() {
return () => h('span', { class: 'badge' }, 'New')
}
}
GOOD:
import { h } from 'vue'
function Badge(props, { slots }) {
return h('span', { class: 'badge' }, slots.default?.())
}
Badge.props = ['variant']
export default Badge