render函数不是模板的升级版,而是绕过模板限制、动态构造结构、深度干预虚拟DOM的底层控制手段;适用于运行时决定组件类型、递归渲染树形控件、注入第三方HTML、封装无渲染组件等场景。

Vue.js 的渲染函数(Render Functions)不是替代模板的“升级版”,而是一种底层控制手段——当你需要绕过模板语法限制、动态构造结构、或深度干预虚拟 DOM 生成过程时,它才真正不可替代。
什么时候必须用 render 函数
模板做不到的事,render 才能做:
- 运行时决定组件类型:比如根据配置项动态渲染 Button、Link 或 Icon,且每个实例的 props、事件、插槽都不同
- 批量生成嵌套层级:如树形控件中,子节点数量和深度完全由数据驱动,递归渲染无法用 v-for + slot 原生表达
- 直接注入第三方 HTML 或富文本:用
domProps: { innerHTML: raw }安全插入已解析的 Markdown 或编辑器输出,不经过 Vue 模板编译流程 - 封装“无渲染”组件(renderless):只提供逻辑和状态,把 UI 完全交给使用者通过
scoped slots或 render 函数自定义,比如表单校验器、拖拽控制器、权限守卫
render 函数的核心写法要点
别被 createElement 的名字吓住,它本质就是 VNode 工厂。现代写法普遍用 h 作为别名(Vue 3 默认支持),参数顺序固定:
-
第一个参数:标签名(
'div')、组件对象(MyButton)、或异步组件工厂函数 -
第二个参数:数据对象,包含
class、style、props、on(事件)、attrs(原生属性)、domProps(如innerHTML)等 -
第三个参数:子节点,可以是字符串、数组(含多个
h()调用)、或混合结构;支持函数式子节点(用于作用域插槽展开)
示例:一个带条件插槽的卡片组件
递归分析 Vue 项目组件依赖,从入口文件生成组件层级图,支持 Vue 2/3,输出组件名、文件路径和属性。适用于分析组件结构、排查依赖或了解项目架构。
立即学习“前端免费学习笔记(深入)”;
render(h) {
const header = this.$slots.header
? this.$slots.header()
: h('h3', this.title);
return h('article', { class: 'card' }, [
header,
h('div', { class: 'body' }, this.$slots.default && this.$slots.default())
]);
}
JSX 让 render 更自然
纯 h() 调用嵌套多层容易变成“括号地狱”。启用 JSX 后,你可以写接近模板的语法,同时保留 JS 全部能力:
- 安装
babel-plugin-transform-vue-jsx(Vue 2)或使用 Vue 3 内置 JSX 支持 - 事件绑定用
onClick而非on:click,props 直接解构传入 - 可直接使用三元、map、if/else —— 不用再包裹在
h()外层判断
例如:
render() {
return (
<div class="list">
{this.items.length > 0
? this.items.map(item => (
<Item key={item.id} {...{ props: item }} />
))
: <p class="empty">No items</p>}
</div>
);
}
避开常见陷阱
render 函数强大,但容易误用:
- 别在 render 里做副作用:不能调用 API、修改 data、触发 store commit —— 它应是纯函数,只返回 VNode
-
key 必须稳定:列表渲染时,
h('li', { key: item.id }, ...)中的 key 不能是索引(i),否则重排会错乱 -
插槽处理要主动:
this.$slots.default是函数,需手动执行;this.$scopedSlots(Vue 2)或context.slots(Vue 3)同理 - 不要为了“高级”而用 render:90% 的业务组件用模板更清晰、易维护、利于 SSR 和 IDE 支持

















