news 2026/6/25 18:47:15

13-Vue2 渲染函数与 JSX

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
13-Vue2 渲染函数与 JSX

Vue2 渲染函数与 JSX

Vue 的模板语法在绝大多数场景下足够使用,但在某些复杂场景下,渲染函数(Render Functions)提供了更灵活的编程能力。配合 JSX 语法,可以像写 React 一样编写 Vue 组件。


一、前言

Vue 推荐在绝大多数情况下使用模板来构建 HTML。然而在某些场景下,你需要完全利用 JavaScript 的编程能力:

  • 需要大量条件判断和循环的动态组件结构
  • 需要精细控制子组件的渲染逻辑
  • 封装高度抽象的组件库

Vue 模板编译

模板

渲染函数

JSX

编译为渲染函数

直接操作 VNode

编译为渲染函数

VNode 树

DOM 更新


二、虚拟 DOM 与 VNode

2.1 什么是虚拟 DOM

虚拟 DOM(Virtual DOM)是真实 DOM 的 JavaScript 对象表示。Vue 通过对比新旧 VNode 树(Diff 算法),最小化地更新真实 DOM。

模板/渲染函数

创建 VNode

Diff 对比

生成补丁

更新真实 DOM

2.2 VNode 的结构

// 简化的 VNode 结构{tag:'div',data:{class:'container',attrs:{id:'app'}},children:[{tag:'h1',text:'标题'},{tag:'p',text:'段落'}],text:undefined,elm:undefined// 对应的真实 DOM 元素}

三、渲染函数基础

3.1 createElement 参数

渲染函数接收createElement(通常简写为h)作为参数:

exportdefault{render(h){returnh('div',{// 数据对象class:{active:this.isActive},style:{color:'red'},attrs:{id:'foo'},domProps:{innerHTML:'<span>HTML</span>'},on:{click:this.handleClick}},[// 子节点数组h('h1','标题'),h('p','内容'),this.items.map(item=>h('span',item.name))]);}};

3.2 createElement 参数详解

h(// {String | Object | Function}// HTML 标签、组件选项或异步组件函数'div',// {Object} 可选,数据对象{// 与 `v-bind:class` 相同class:{active:true,'text-danger':false},// 与 `v-bind:style` 相同style:{fontSize:'14px',color:'red'},// 普通 HTML 属性attrs:{id:'foo',href:'#'},// 组件 propsprops:{myProp:'bar'},// DOM 属性domProps:{innerHTML:'baz'},// 事件监听器,支持 ~ 和 ! 修饰符on:{click:this.clickHandler,'~keyup':this.keyupHandler,'!click':this.captureClick},// 仅用于组件,原生事件nativeOn:{click:this.nativeClickHandler},// 自定义指令directives:[{name:'my-directive',value:'2',expression:'1 + 1'}],// 作用域插槽格式:{ name: props => VNode | Array<VNode> }scopedSlots:{default:props=>h('span',props.text)},// 插槽名称slot:'name-of-slot',// 其他顶级属性key:'myKey',ref:'myRef'},// {String | Array} 子节点['先写一些文字',h('h1','一则头条')]);

四、模板 vs 渲染函数

4.1 模板写法

<template><divclass="list-container"><h2v-if="title">{{ title }}</h2><ul><liv-for="item in items":key="item.id":class="{ active: item.active }"@click="select(item)">{{ item.name }}</li></ul></div></template>

4.2 等价渲染函数

exportdefault{props:['title','items'],methods:{select(item){this.$emit('select',item);}},render(h){returnh('div',{class:'list-container'},[this.title?h('h2',this.title):null,h('ul',this.items.map(item=>h('li',{key:item.id,class:{active:item.active},on:{click:()=>this.select(item)}},item.name)))]);}};

五、JSX 语法

5.1 JSX 配置

需要配置 Babel 插件支持 JSX:

npminstall@vue/babel-preset-jsx @vue/babel-helper-vue-jsx-merge-props-D
// babel.config.jsmodule.exports={presets:['@vue/babel-preset-jsx']};

5.2 JSX 基本用法

exportdefault{data(){return{msg:'Hello JSX'};},render(){return(<divclass="container"><h1>{this.msg}</h1><p>使用JSX编写 Vue 组件</p></div>);}};

5.3 JSX 中的指令

exportdefault{render(){constitems=['a','b','c'];return(<div>{/* v-if -> 三元表达式 */}{this.show?<span>显示</span>:null}{/* v-for -> Array.map */}<ul>{items.map((item,index)=>(<li key={index}>{item}</li>))}</ul>{/* v-model -> value + onInput */}<input value={this.inputValue}onInput={e=>this.inputValue=e.target.value}/>{/* v-on -> onXxx */}<button onClick={this.handleClick}>点击</button>{/* 事件修饰符 -> 原生处理 */}<button onClick_stop={this.handleClick}>阻止冒泡</button><button onClick_prevent={this.handleClick}>阻止默认</button>{/* 插槽 */}<MyComponent><div slot="header">头部</div><div>默认内容</div></MyComponent>{/* 作用域插槽 */}<MyComponent scopedSlots={{default:({text})=><span>{text}</span>}}/></div>);}};

六、函数式组件

6.1 什么是函数式组件

函数式组件是无状态(没有响应式数据)且无实例(没有 this 上下文)的组件。它们只接收 props 并返回 VNode:

// 函数式组件(JSX)exportdefault{functional:true,props:['level','title'],render(h,context){const{props,slots,listeners}=context;returnh(`h${props.level}`,{on:listeners},[props.title,slots().default]);}};

6.2 函数式组件优势

  • 渲染开销低(无响应式系统)
  • 适合纯展示组件
  • 可作为高阶组件包装器
// 高阶组件示例:添加点击跟踪exportdefaultfunctionwithTracking(Component){return{functional:true,render(h,context){constlisteners={...context.listeners,click:(...args)=>{console.log('组件被点击');context.listeners.click&&context.listeners.click(...args);}};returnh(Component,{...context.data,on:listeners},context.children);}};}

七、插槽与渲染函数

7.1 访问插槽

exportdefault{render(h){// 访问默认插槽constdefaultSlot=this.$slots.default;// 访问命名插槽constheaderSlot=this.$slots.header;// 访问作用域插槽constscoped=this.$scopedSlots.default;returnh('div',[h('header',headerSlot),h('main',defaultSlot),scoped?scoped({text:'作用域数据'}):null]);}};

八、常见场景

8.1 动态表格列

exportdefault{props:['columns','data'],render(h){returnh('table',[h('thead',[h('tr',this.columns.map(col=>h('th',col.title)))]),h('tbody',this.data.map(row=>h('tr',this.columns.map(col=>h('td',{domProps:{innerHTML:col.render?col.render(h,row[col.key],row):row[col.key]}})))))]);}};

8.2 动态组件工厂

// 根据配置动态生成表单组件exportdefault{props:['config'],render(h){returnh('form',this.config.fields.map(field=>{constcomponentMap={input:'el-input',select:'el-select',date:'el-date-picker'};returnh(componentMap[field.type]||'input',{props:{value:field.value},on:{input:val=>this.$emit('change',field.key,val)}});}));}};

九、总结

方式适用场景优点缺点
模板绝大多数场景直观、声明式、易维护复杂逻辑受限
渲染函数高度动态内容完全编程控制可读性较差
JSX熟悉 React 的开发者接近 JavaScript 语法需要额外配置
函数式组件纯展示组件性能最优无状态、无生命周期

建议:优先使用模板,遇到模板难以表达的场景再考虑渲染函数或 JSX。

下一章我们将学习 Vue 项目的构建与工程化,掌握 Vue CLI 和 Webpack 配置。


十、练习

  1. 使用渲染函数重写一个v-if+v-for组合的复杂组件
  2. 用 JSX 实现一个可复用的List组件,支持自定义渲染项
  3. 创建一个函数式组件Heading,根据 level 渲染 h1-h6
  4. 实现一个动态表单组件,根据 JSON 配置渲染不同类型的表单项
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/25 18:41:37

计算机毕业设计之基于SSM的大学生兴趣组管理系统

系统根据现有的管理模块进行开发和扩展&#xff0c;采用面向对象的开发的思想和结构化的开发方法对大学生兴趣组管理的现状进行系统调查。采用结构化的分析设计&#xff0c;该方法要求结合一定的图表&#xff0c;在模块化的基础上进行系统的开发工作。在设计中采用“自下而上”…

作者头像 李华
网站建设 2026/6/25 18:40:54

如何快速打造你的专属虚拟桌面伴侣:Mate Engine免费开源指南

如何快速打造你的专属虚拟桌面伴侣&#xff1a;Mate Engine免费开源指南 【免费下载链接】Mate-Engine A free Desktop Mate alternative with a lightweight interface and custom VRM support, though with more features. 项目地址: https://gitcode.com/gh_mirrors/ma/Ma…

作者头像 李华
网站建设 2026/6/25 18:38:16

第一幕:那些年,我们一起手动执行的 SQL

本地开发改了 Model&#xff0c;然后把改表的 SQL 语句保存到一个 txt 或者 sql 文件里。上线的时候&#xff0c;战战兢兢地连上 生产数据库 &#xff0c;复制、粘贴、回车&#xff0c;一气呵成。运气好&#xff0c;一切顺利&#xff1b;运气不好&#xff0c;一个语法错误或者字…

作者头像 李华
网站建设 2026/6/25 18:37:24

AI危险自信的本质与四步事实校验法

1. 项目概述&#xff1a;当AI回答得越流畅&#xff0c;你越该按下暂停键 “ChatGPT说的都对”——这句话我听过不下五十次&#xff0c;来自产品经理、高校教师、创业公司CTO&#xff0c;甚至还有正在写毕业论文的研究生。他们不是没警惕性&#xff0c;而是被AI那种近乎人类的语…

作者头像 李华
网站建设 2026/6/25 18:37:08

MitoHiFi:三步搞定PacBio HiFi数据的线粒体基因组组装

MitoHiFi&#xff1a;三步搞定PacBio HiFi数据的线粒体基因组组装 【免费下载链接】MitoHiFi Find, circularise and annotate mitogenome from PacBio assemblies 项目地址: https://gitcode.com/gh_mirrors/mi/MitoHiFi 你是否曾为线粒体基因组组装而烦恼&#xff1f;…

作者头像 李华
网站建设 2026/6/25 18:32:48

MCP协议深度解析:从原理到实战,打造你的第一个AI工具集成

引言 当ChatGPT等大模型惊艳世界之后&#xff0c;开发者面临的下一个难题是&#xff1a;如何让AI真正触及企业数据、调用外部工具、记住会话上下文&#xff1f;传统的做法是各自编写复杂的插件系统或自定义HTTP API&#xff0c;导致兼容性差、重复劳动严重。2024年11月&#x…

作者头像 李华