Code前端首页关于Code前端联系我们

Vue2的render函数该咋用?和template区别在哪?

terry 1天前 阅读数 15 #Vue
文章标签 render函数 template

不少刚接触Vue2的小伙伴,面对模板(template)和render函数总会犯难:这俩到底有啥不一样?啥时候非得用render?写的时候又该注意啥?其实render函数是Vue2里“更底层、更灵活”的渲染方式,能解决模板搞不定的复杂场景,今天咱们用问答形式,把render函数的关键知识点拆明白,从基础到场景一次讲透~

Vue2的render函数是干啥的?

简单说,render函数是用来生成虚拟DOM(VNode)的工具,Vue内部其实会把咱们写的template模板“编译”成render函数——你可以理解为,template是“可视化”的写法,而render是更底层的“手动拼装VNode”的方式。

举个例子:如果用template写<div>{{ msg }}</div>,Vue会偷偷把它变成类似render(h) { return h('div', this.msg) }的逻辑,所以render函数相当于“跳过模板编译,自己直接拼VNode”,能让咱们手动控制渲染逻辑。

那啥时候必须自己写render?比如要动态渲染未知组件(后端返回组件名)、做高度自定义的组件库(像Element UI的表格列渲染)、或者处理极端复杂的条件/循环逻辑——这些场景下,template的指令(v-if、v-for)不够灵活,render函数的JS逻辑能帮咱们精准控制每一个VNode的生成。

render函数和template核心区别是啥?

得从编译阶段、灵活性、适用场景这三点掰扯:

编译阶段不同

template是“先写模板,Vue帮你编译成render函数”;而render函数是“你自己直接写生成VNode的逻辑”,打个比方:template是“点外卖(别人做好给你)”,render是“自己买菜做饭(全程自己控制)”。

灵活性不在一个维度

template靠v-ifv-for、这些指令写逻辑;但render函数直接用JS写逻辑——循环可以用for/map,条件可以用if-else/三目运算,甚至能动态决定渲染哪个组件。

比如要根据用户权限渲染不同组件,template得写一堆v-if

<template>
  <AdminComponent v-if="isAdmin" />
  <UserComponent v-else />
</template>

但render函数用JS逻辑更简洁:

render(h) {
  return h(this.isAdmin ? 'AdminComponent' : 'UserComponent');
}

适用场景有分工

template适合常规页面/组件(结构固定、逻辑简单),写起来像HTML,容易上手;
render函数适合复杂动态场景(组件库开发、动态解析JSON生成页面、性能敏感逻辑),需要精准控制VNode的时候。

咋写一个基础的render函数?

Vue2里,组件的render选项是个函数,参数是h(全称createElement,用来创建VNode)。h函数接收三个参数:标签名/组件、数据对象(属性/样式/事件等)、子节点

先看最基础的结构:

export default {
  render(h) {
    // h(标签名, 数据对象, 子节点)
    return h('div', {
      // 数据对象:放属性、样式、事件、class等
      class: 'box', // class名
      style: { color: 'red' }, // 内联样式
      on: { click: this.handleClick } // 事件
    }, [
      // 子节点可以是字符串、或嵌套的h函数
      '这是文本内容',
      h('p', '这是子元素')
    ]);
  },
  methods: {
    handleClick() {
      console.log('点击了div');
    }
  }
};

想渲染子组件也一样,把第一个参数换成组件即可:

import MyComponent from './MyComponent.vue';
export default {
  components: { MyComponent },
  render(h) {
    return h(MyComponent, {
      props: { msg: '传给子组件的内容' }, // 传props
      on: { customEvent: this.handleEvent } // 监听子组件事件
    });
  },
  methods: {
    handleEvent() { ... }
  }
};

重点记h函数的参数规则:

  • 第一个参数:可以是字符串(如'div')、组件选项对象(如MyComponent)、异步组件(如() => import('./AsyncComponent.vue'));
  • 第二个参数:数据对象,常用的有classstylepropson(事件)、attrs(非props属性)、key(列表渲染必加,避免diff错误);
  • 第三个参数:子节点,数组形式,每个子节点也是h函数调用结果,或字符串。

render函数咋处理动态逻辑?

render函数的灵魂是“用JS写逻辑”,所以循环、条件、动态组件这些场景,比template更丝滑。

循环渲染列表

比如根据数组list渲染多个<li>

data() {
  return {
    list: ['苹果', '香蕉', '橘子']
  };
},
render(h) {
  return h('ul', this.list.map((item, index) => {
    // 每个li加key(数据对象里的key属性)
    return h('li', { key: index }, item);
  }));
}

复杂条件渲染

如果有三四种条件分支,template的v-if会写得很臃肿,render用JS逻辑更清爽:

render(h) {
  if (this.status === 'loading') {
    return h('div', '加载中...');
  } else if (this.status === 'error') {
    return h('div', '请求失败');
  } else {
    return h('div', '数据内容:' + this.data);
  }
}

动态渲染组件

后端返回一个组件名componentName,要动态渲染:

render(h) {
  // 假设components里注册了所有可能的组件
  const Component = this.$options.components[this.componentName];
  return h(Component, { props: { data: this.data } });
}

结合JSX,render函数能写得更顺手?

h函数写复杂结构时,嵌套多了会很绕(比如写一个包含多个子组件和嵌套标签的结构,h函数层层嵌套像“ Callback Hell ”),这时候JSX能救场——它让render函数的写法更像HTML,直观又简洁。

Vue2里用JSX需要装babel插件(比如@vue/babel-plugin-jsx),配置后就能在render里写JSX语法:

// 安装插件后,render函数可以这样写
render() {
  return (
    <div class="container" onClick={this.handleClick}>
      <p>{this.msg}</p>
      <MyComponent msg={this.msg} onCustomEvent={this.handleEvent} />
    </div>
  );
}

对比纯h函数,JSX的结构和HTML几乎一样,写起来效率高多了,而且JSX里能直接嵌入JS表达式(用包起来),比如循环、条件都能写:

render() {
  return (
    <ul>
      {this.list.map((item, index) => (
        <li key={index}>{item}</li>
      ))}
    </ul>
  );
}

简单说:JSX是h函数的“语法糖”,让render函数的写法更友好,如果写复杂组件,强烈建议用JSX!

哪些场景必须用render函数?

不是所有场景都需要render,但这四类场景,template真的搞不定:

开发通用组件库

比如写表格组件(列内容自定义)、弹窗组件(内容插槽高度自定义),像Element UI的<el-table>,每一列的内容允许用户用render函数定制:

// 表格列配置
columns: [
  { '姓名',
    render: (row) => <span>{row.name}</span> // JSX写法
  },
  { '操作',
    render: (row) => (
      <button onClick={() => this.edit(row)}>编辑</button>
    )
  }
]

这种“用户自定义渲染逻辑”的需求,template的插槽玩不转,必须靠render函数。

动态解析JSON生成页面

如果后端返回一个JSON,描述页面结构(比如{ tag: 'div', children: [...] }),template没法动态解析标签,但render函数可以递归遍历JSON,生成对应的VNode:

// 假设后端返回的结构是treeData
render(h) {
  function buildVNode(node) {
    return h(node.tag, node.attrs, node.children.map(buildVNode));
  }
  return buildVNode(this.treeData);
}

性能敏感场景

比如渲染巨型列表(几千条数据),template的v-for会有编译开销,而render函数可以手动优化VNode生成(比如复用静态节点、减少不必要的响应式追踪),虽然Vue本身有虚拟列表优化,但极端场景下,render的手动控制能更高效。

跨平台渲染(拓展思路)

Vue2主要用于Web,但如果想让同一份逻辑生成小程序组件、Native组件,render返回的VNode结构更容易被不同平台的渲染器解析(类似React Native的思路),这种场景下,template的HTML语法就不适用了,render的JS逻辑更通用。

用render函数容易踩哪些坑?

写render时,这些“暗坑”稍不注意就栽进去:

响应式数据丢失

如果直接操作VNode(比如node.children = '新内容'),Vue的响应式系统没法跟踪变化,页面不会更新。解决方法:始终让数据驱动VNode,把要渲染的内容存在datacomputed里,靠响应式触发更新。

子节点没加key,diff出问题

列表渲染时,子节点必须加key(在数据对象里写{ key: 唯一值 }),否则Vue的diff算法会认错节点,导致渲染错误,比如循环渲染时,一定要给每个子节点配key

// 正确写法:加key
this.list.map((item, index) => h('li', { key: item.id }, item.name));

样式、事件绑定写错位置

新手常把事件写到attrs里(比如attrs: { onclick: ... }),但Vue里事件必须放在on对象里;样式要写成对象(style: { color: 'red' }),不能写成字符串。

  • 事件 → on: { 事件名: 处理函数 }
  • 样式 → style: { CSS属性: 值 }
  • class → 可以是字符串、数组、对象

动态组件的生命周期问题

如果用h(AsyncComponent, ...)渲染异步组件,要注意组件的销毁时机,如果频繁切换组件,没正确销毁可能导致内存泄漏。解决方法:确保组件在不需要时能被Vue的销毁钩子正常清理,或者用<keep-alive>缓存(但要权衡性能)。

咋调试render函数生成的结构?

写render时,难免遇到VNode生成不对、事件不触发这些问题,调试方法得备上:

用Vue DevTools看VNode

Vue DevTools的Components面板里,能看到组件渲染后的VNode结构,包括属性、事件、子节点,哪里不对,一眼能瞄出来。

打印h函数的输出

在render函数里console.log生成的VNode,看看结构是否符合预期:

render(h) {
  const vnode = h('div', '内容');
  console.log(vnode); // 打印VNode对象,检查tag、children、data等
  return vnode;
}

对比template编译后的render

如果不确定template怎么转成render,可以新建一个用template的组件,然后在浏览器里看Vue编译后的render函数(需要开启开发模式的源码映射),对比自己写的render有啥差异。

Vue2升级Vue3后,render函数有啥变化?

Vue3的Composition API里,render函数写法变了,但核心逻辑(生成VNode)没大变化,主要差异是:

  • h函数需要导入:Vue3里要从vue导入h,而Vue2里h是render函数的参数;
  • 参数结构更清晰:Vue3的h函数参数中,propsattrs分离更明显,事件统一放在on里(还支持oncecapture等修饰符);
  • render的位置:Vue3里常用setup函数返回render,或者用<script setup>结合JSX。

掌握Vue2的render函数逻辑,对理解Vue3的渲染机制帮助很大——毕竟都是围绕VNode玩花样~

最后总结下:render函数是Vue2里“手动掌控渲染”的利器,适合复杂动态场景;template是“开箱即用”的常规选择,如果想写组件库、处理极端逻辑,或者想更深入理解Vue的渲染原理,把render函数吃透准没错~ 现在可以动手写个小例子,比如用render+JSX做个动态列表,感受下灵活度有多高~

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门