VueJS - 渲染函数
Vue 3 - 渲染函数与 JSX
Section titled “Vue 3 - 渲染函数与 JSX”虽然 Vue 的模板功能强大,足以覆盖绝大多数用例,但有时您需要 JavaScript 的完全编程能力来创建动态组件。这时就需要用到渲染函数。Vue 3 提供了 h 函数(“hyperscript” 的缩写),用于在渲染函数或 setup 函数中创建虚拟 DOM 节点。
渲染函数通常用于高级场景,或者在编写需要比模板提供更高灵活性的高度可重用组件时使用。
为什么要使用渲染函数?
Section titled “为什么要使用渲染函数?”- 对组件结构具有完全的编程控制能力。
- 创建需要根据 Props 动态决定 HTML 标签的组件。
- 在非常特定、罕见的情况下进行性能优化(尽管 Vue 的模板编译器已经高度优化)。
- 与期望 JavaScript 函数进行渲染的库集成。
对于大多数应用而言,使用单文件组件(SFCs)中的模板是推荐且更易维护的方法。
h 函数是渲染函数的核心。它通常从 ‘vue’ 导入。
import { h } from 'vue'
// h(type, props, children)
// type: String (HTML tag), Object (Component), Function (Async Component)// props: Object with attributes, props, event listeners etc.// children: String (text) or Array of VNodes (other h() calls) or Slot function示例:动态标题组件
Section titled “示例:动态标题组件”让我们使用 Vue 3 的 h 函数重现一个类似原始示例的组件。这个组件将渲染一个带动态文本内容和样式的标题(h1 到 h6)。
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Vue 3 Render Function Example</title> <script src="https://unpkg.com/vue@3"></script></head><body> <div id="app"> <dynamic-heading :level="1" style="color: blue;">Main Title</dynamic-heading> <dynamic-heading :level="3" class="sub-title">Subtitle</dynamic-heading> <dynamic-heading :level="currentLevel" :style="dynamicStyle">Dynamic Level Heading</dynamic-heading> <button @click="increaseLevel">Increase Level (Current: {{ currentLevel }})</button> </div>
<script> const { createApp, h, ref, computed } = Vue;
const app = createApp({ setup() { const currentLevel = ref(4); const dynamicStyle = computed(() => ({ color: currentLevel.value > 3 ? 'red' : 'green', borderBottom: '1px solid black' }));
function increaseLevel() { if (currentLevel.value < 6) { currentLevel.value++; } else { currentLevel.value = 1; // Cycle back } } return { currentLevel, dynamicStyle, increaseLevel }; } });
// 使用渲染函数的动态标题组件 app.component('dynamic-heading', { props: { level: { // 标题级别 (1-6) type: Number, required: true, validator: (value) => value >= 1 && value <= 6 } // 其他属性如 'style', 'class' 通过渲染函数上下文的 $attrs 自动传递 }, setup(props, { slots, attrs }) { // 访问 props, slots, attrs // 在 setup 中定义渲染函数 return () => { // 返回渲染函数本身 const tag = `h${props.level}`; // 计算标签名
// 使用 h() 创建 VNode return h( tag, // 元素类型 attrs, // 传递非 props 属性 (如 style, class) slots.default ? slots.default() : [] // 渲染默认插槽内容 ); }; } // --- Options API 等价写法 --- /* props: { level: { type: Number, required: true } }, render() { const tag = `h${this.level}`; return h( tag, this.$attrs, // 传递属性 this.$slots.default ? this.$slots.default() : [] // 访问插槽 ); } */ });
app.mount('#app'); </script> <style> .sub-title { font-style: italic; color: gray; } </style></body></html>- 我们从 Vue 导入
h(或在 CDN 示例中通过全局的Vue.h访问)。 dynamic-heading组件接受一个levelprop (1-6)。- 在 Composition API 的
setup函数中,我们返回另一个函数。这个返回的函数才是真正的渲染函数。 - 在渲染函数内部,我们根据
props.level动态确定标签名(例如'h1'、'h2')。 - 我们调用
h(tag, attrs, slots.default()): tag:动态确定的 HTML 标签字符串。attrs:这个对象(由setup的第二个参数context提供,通过解构获得{ attrs })包含传递给组件但未声明为 props 的所有属性(例如本例中的style和class)。传递这些属性可以确保应用在组件标签上的样式和类能按预期工作。slots.default():这访问组件标签之间传递的默认插槽内容(例如 ‘Main Title’)。我们将其作为函数调用以获取插槽内容的 VNodes 数组。我们添加了一个检查 (slots.default ? ... : []) 以防没有提供插槽内容。- Options API 的等价写法直接使用
render()方法,通过this.level访问 props,通过this.$attrs访问属性,通过this.$slots访问插槽。
JSX 替代方案
Section titled “JSX 替代方案”Vue 也支持在渲染函数中使用 JSX (JavaScript XML),这感觉更像编写模板。这需要构建工具配置(例如为 Vite 添加 @vitejs/plugin-vue-jsx)。
// 使用 JSX 的示例 (需要构建设置)import { defineComponent } from 'vue';
export default defineComponent({ props: { level: { type: Number, required: true } }, setup(props, { slots, attrs }) { const Tag = `h${props.level}`; // 注意: JSX 标签需要用大写变量 return () => ( <Tag {...attrs}> { slots.default ? slots.default() : '' } </Tag> ); }});JSX 允许直接在 JavaScript 中编写类似 HTML 的语法,它会被编译成 h 函数调用。注意,动态标签名需要存储在一个大写的变量 (Tag) 中,以便 JSX 将其识别为动态组件/标签。
当模板功能不足时,渲染函数和 JSX 提供了最大的灵活性。然而,它们比模板更冗长,不够声明式。在复杂的动态渲染场景或构建高度可配置的底层组件时,请谨慎使用它们。
更多详情,请参考官方文档:Render Functions & JSX。