Vue | Vue深入浅出——Vue中的render函数详解

1.render函数

在编写vue单文件的大多数情况下,我们都是使用template模板来创建HTML。然而在一些条件判断比较复杂的场景下,使用JavaScript去描绘HTML的生成逻辑会显得更加的简洁直观。

使用Vue官网的例子来简单说明:

如果自己在开发的时候,编写的每个标题(包括h1~h6)都需要带锚点,如下所示:

<h1>
  <a name="hello-world" href="#hello-world">
    Hello world!
  </a>
</h1>

如果用template模板进行编写,会如下所示:

<template>
  <h1 v-if="level === 1">
    <anchor :name="name" :content="content"></anchor>
  </h1>
  <h2 v-else-if="level === 2">
    <anchor :name="name" :content="content"></anchor>
  </h2>
  <h3 v-else-if="level === 3">
    <anchor :name="name" :content="content"></anchor>
  </h3>
  <h4 v-else-if="level === 4">
    <anchor :name="name" :content="content"></anchor>
  </h4>
  <h5 v-else-if="level === 5">
    <anchor :name="name" :content="content"></anchor>
  </h5>
  <h6 v-else-if="level === 6">
    <anchor :name="name" :content="content"></anchor>
  </h6>
</template>
<script>
export default {
  name:'anchor-header',
  props:{
    level:Number,
    name:String,
    content:String
  },
  components:{
    'anchor':{
      props:{
        content:String,
        name:String
      },
      template:'<a :id="name" :href="`#${name}`"> {{content}}</a>'
    }
  }
}
</script>

显然代码冗长累赘。但如果用render函数来编写则如下所示:

<script>
export default {
  name:'anchor-header',
  props:{
    level:Number,
    name:String,
    content:String
  },
  render:function(createElement){
    const anchor={
      props:{
        content:String,
        name:String
      },
      template:'<a :id="name" :href="`#${name}`"> {{content}}</a>'
    }
    const anchorEl=createElement(anchor,{
      props:{
        content:this.content,
        name:this.name
      }
    })
    const el=createElement(
      `h${this.level}`,
      [anchorEl]
    )
    return el
  }
}
</script>

可见通过render函数编写出的逻辑更加简洁且可读性更高。

每一个render函数都要return一个VNode类型的变量,是Vue中自定义的虚拟节点(virtual node),用于替换挂载元素$el。

Vue 选项中的 render 函数若存在,则 Vue 构造函数不会从 template 选项或通过 el 选项指定的挂载元素中提取出的 HTML 模板编译渲染函数。

但自己实践在vue单文件实践后发现,如果同时存在templaterender,生成的html会以template的逻辑为主,奇怪。

上述例子的代码可以知道,render函数使用的场景是,要根据不同条件切换不同HTML标签,可以使用render函数。或者条件判断较多的template中,用渲染函数编写会让代码的可读性更高的情况下,也推荐使用render函数。

下面分析上面代码中出现的createElement函数。

2.createElement

createElement用于创建且return一个VNode类型的变量(虚拟节点),以下是该函数的传入参数:

(1)一个 HTML 标签名、组件选项对象或者resolve前面两种之一类型的async函数。必填。

例如,传入'div'表示想创建一个html标签为div的VNode;如果传入'transition'代表创建transition组件。

上面作为粒子的代码就是根据传入的类型为Number的参数level,用模板字符串拼接成标签名称。传入level为1则拼接出来的HTML标签名称为h1。代表要创建html标签为h1的VNode。

(2)所创建的VNode中所需参数为属性的数据对象。可选。

{
  class,
  style,
  attrs,
  props,
  domProps,
  on,
  nativeOn,
  directives:[
    {
      name,
      value,
      expression,
      arg,
      modifiers
    }
  ],
  scopedSlots:{
    default:props=>createElement()
  },
  slot,
  key,
  ref,
  refInFor
}

大部分属性和vue组件中存在的属性的作用一样,我就只挑几个比较特殊的属性来说明:

**nativeOn:**用于监听原生事件,而不是组件内使用。例如:

nativeOn: {click: this.nativeClickHandler}

相当于@click.native="nativeClickHandler"

scopedSlots:定义作用域插槽的内部的内容:

格式为:{ name: props => VNode | Array<VNode> }

举一个例子:

<script>
export default {
  render (createElement) {
    var component = {
      template: `<div>
        <slot></slot>
        <slot name="foo"></slot>
      </div>`
    }
    return createElement(component, {
      scopedSlots: {
        default: props => createElement('span', '456'),
        foo: props => createElement('span', '789')
      },
    })
  }
}
</script>

最后渲染出来的html效果如下:

<div>
     <span>456</span>
     <span>789</span>
</div>

scope:如果要生成的组件要插入到,需为插槽指定名称。

举个例子

<script>
export default {
  render (createElement) {
    var component = {
      template: `<div>
        <slot></slot>
        <slot name="foo"></slot>
      </div>`
    }
    const childrenEl = createElement('span', { slot: 'foo' }, '123')
    return createElement(component, {
      scopedSlots: {
        default: props => createElement('span', '456')
        // foo: props => createElement('span', '789')      },
    }, [childrenEl])
  }
}
</script>

最后渲染出来的html效果如下:

<div>
     <span>456</span>
     <span>123</span>
</div>

注意:如果去掉上面例子的代码中// foo: props => createElement('span', '789')的注释,则slot="foo"插槽中显示内容依然为<span>789</span>

**  refInFor:**如果你在渲染函数中给多个元素都应用了相同的 ref 名,那么 `$refs.myRef` 会变成一个数组。

(3)子级虚拟节点 (VNodes)。如果传入的是VNode则要用列Array传入,另外也可以使用字符串来生成“文本虚拟节点”。可选。

3.函数式组件

函数式组件相比于一般的vue组件而言,最大的区别是非响应式的它不会监听任何数据,也没有实例(因此没有状态,意味着不存在诸如created,mounted的生命周期)。好处是因只是函数,故渲染开销也低很多。

把开头的例子改成函数式组件,代码如下:

<script>
export default {
  name:'anchor-header',
  functional:true, // 以functional:true声明该组件为函数式组件
  props:{
    level:Number,
    name:String,
    content:String
  },
  // 对于函数式组件,render函数会额外传入一个context参数用来表示上下文,即替代this。函数式组件没有实例,故不存在this
  render:function(createElement,context){ 
    const anchor={
      props:{
        content:String,
        name:String
      },
      template:'<a :id="name" :href="`#${name}`"> {{content}}</a>'
    }
    const anchorEl=createElement(anchor,{
      props:{
        content:context.props.content, //通过context.props调用props传入的变量
        name:context.props.name
      }
    })
    const el=createElement(
      `h${context.props.level}`,
      [anchorEl]
    )
    return el
  }
}
</script>

渲染函数 & JSX — Vue.js:更多关于函数式组件内容请看官网函数式组件

4.element-ui的el-row组件

最后以el-row组件的源码来分析,该源码的渲染逻辑在render函数上,非常简洁明了:

export default {
  name: 'ElRow',

  componentName: 'ElRow',

  props: {
    tag: {
      type: String,
      default: 'div'
    },
    gutter: Number,
    type: String,
    justify: {
      type: String,
      default: 'start'
    },
    align: {
      type: String,
      default: 'top'
    }
  },

  computed: {
    style() {
      const ret = {};

      if (this.gutter) {
        ret.marginLeft = `-${this.gutter / 2}px`;
        ret.marginRight = ret.marginLeft;
      }

      return ret;
    }
  },

  render(h) {
    return h(this.tag, {
      class: [
        'el-row',
        this.justify !== 'start' ? `is-justify-${this.justify}` : '',
        this.align !== 'top' ? `is-align-${this.align}` : '',
        { 'el-row--flex': this.type === 'flex' }
      ],
      style: this.style
    }, this.$slots.default);
  }
};

对着el-row组件传入参数的说明图来解释:

直接从render函数处进行分析,

1.传入第一个参数为this.tag,用于根据参数生成对应的html标签。

2.第二个参数中传入classstyle是根据props中的type,gutter,justify,align生成的。

3.第三个参数传入子节点。此处通过this.$slots.default拿到传入的子节点。例如:

<el-row>
    <div>123</div>
</el-row>

此时,this.$slots.default 获取的数据则是一个包含上面<div>123</div>的VNode的数组。

以下内容来自官网:

拓展:slots() 和 children 对比

你可能想知道为什么同时需要 slots() childrenslots().default 不是和 children 类似的吗?在一些场景中,是这样——但如果是如下的带有子节点的函数式组件呢?

<my-functional-component>
  <p v-slot:foo>
    first
  </p>
  <p>second</p>
</my-functional-component>

对于这个组件,children 会给你两个段落标签,而 slots().default 只会传递第二个匿名段落标签,slots().foo 会传递第一个具名段落标签。同时拥有 children slots(),因此你可以选择让组件感知某个插槽机制,还是简单地通过传递 children,移交给其它组件去处理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/875886.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

RabbitMQ(高阶使用)延时任务

文章内容是学习过程中的知识总结&#xff0c;如有纰漏&#xff0c;欢迎指正 文章目录 1. 什么是延时任务&#xff1f; 1.1 和定时任务区别 2. 延时队列使用场景 3. 常见方案 3.1 数据库轮询 优点 缺点 3.2 JDK的延迟队列 优点 缺点 3.3 netty时间轮算法 优点 缺点 3.4 使用消息…

2024非常全的接口测试面试题及参考答案

一、前言 接口测试最近几年被炒的火热了&#xff0c;越来越多的测试同行意识到接口测试的重要性。接口测试为什么会如此重要呢&#xff1f; 主要是平常的功能点点点&#xff0c;大家水平都一样&#xff0c;是个人都能点&#xff0c;面试时候如果问你平常在公司怎么测试的&…

【MPC】无人机模型预测控制复现Data-Driven MPC for Quadrotors项目(Part 1)

无人机模型预测控制复现Data-Driven MPC for Quadrotors项目 参考链接背景和问题方法与贡献实验结果安装ROS创建工作空间下载RotorS仿真器源码和依赖创建Python虚拟环境下载data_driven_mpc仓库代码下载并配置ACADO求解器下载并配置ACADO求解器的Python接口下载并配置rpg_quadr…

计算机网络八股总结

这里写目录标题 网络模型划分&#xff08;五层和七层&#xff09;及每一层的功能五层网络模型七层网络模型&#xff08;OSI模型&#xff09; 三次握手和四次挥手具体过程及原因三次握手四次挥手 TCP/IP协议组成UDP协议与TCP/IP协议的区别Http协议相关知识网络地址&#xff0c;子…

学习笔记 - 知识图谱的符号表示方法

学习笔记 - 知识图谱的符号表示方法 说明&#xff1a; 首次发表日期&#xff1a;2024-09-13个人阅读学习并摘录成笔记 知识表示的相关名词定义 以下内容摘录自 Knowledge Graphs Applied 2.3小节&#xff0c;然后AI翻译人工润色。 实体&#xff08;Entities&#xff09;—表…

共享单车轨迹数据分析:以厦门市共享单车数据为例(四)

副标题&#xff1a;共享单车与地铁接驳距离探究——以厦门市为例 关于轨道交通站点接驳范围的研究早已屡见不鲜&#xff0c;通常认为以站点为圆心、800米作为地铁站直接的服务范围是合理的。近年来&#xff0c;随着轨道、公交和慢行交通三网融合概念的提出&#xff0c;慢行交通…

【人工智能】Transformers之Pipeline(十七):文本分类(text-classification)

目录 一、引言 二、文本分类&#xff08;text-classification&#xff09; 2.1 概述 2.2 DistilBERT—BERT 的精简版&#xff1a;更小、更快、更便宜、更轻便 2.3 应用场景​​​​​​​ 2.4 pipeline参数 2.4.1 pipeline对象实例化参数 2.4.2 pipeline对象使用参数 …

探索2B法则—趋势反转的高效策略

在之前&#xff0c;我们曾讲过123法则&#xff0c;这是一种基于道氏理论的分析方法&#xff0c;用于确认趋势反转和识别开仓信号。而今日我们就再来聊聊与之相似的2B法则&#xff0c;这是123法则的一种变形&#xff0c;用途和123法则一样&#xff0c;但比之成功率会更高&#x…

白盒测试与黑盒测试

✨博客主页&#xff1a; https://blog.csdn.net/m0_63815035?typeblog &#x1f497;《博客内容》&#xff1a;.NET、Java.测试开发、Python、Android、Go、Node、Android前端小程序等相关领域知识 &#x1f4e2;博客专栏&#xff1a; https://blog.csdn.net/m0_63815035/cat…

GIS在线监测SF6密度微水定量检漏仪传感器专用波纹管免焊接格兰头

SF6密度微水在线监测装置配套M12母头5孔格兰头穿波纹管连接器&#xff0c;该传感器能够监测SF6气体的露点&#xff0c;从而实现对SF6气体在线监测。 原理 SF6露点在线监测传感器是一种可以监测SF6气体露点的传感器它的工作原理是通过吸收SF6气体中的水分子来测量SF6气体的露点。…

k8s 资源管理

文章目录 ResourceQuota什么是资源配额定义一个ResourceQuotaResourceQuota的使用 LimitRangeLimitRange的用途示例1&#xff1a;配置默认的requests和limits示例2&#xff1a;配置requests和limits的范围 QoS什么是服务质量保证示例1&#xff1a;实现QoS为Guaranteed的Pod示例…

git的快速合并fast-forward merge详解

文章目录 1. 什么是快进合并&#xff1f;2. 快进合并的前提条件3. 快进合并的工作原理3.1 示例场景&#xff1a;3.2 使用命令&#xff1a;3.3 快进合并的视觉效果&#xff1a; 4. 快进合并的优点5. 快进合并的缺点6. 快进合并 vs 非快进合并6.1 非快进合并&#xff1a;6.2 非快…

splice用法

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

Google推出Data Commons解决AI“幻觉”

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

使用LDAP登录GitLab

使用LDAP登录GitLab gitlab.rb 配置如下 gitlab_rails[ldap_enabled] true #gitlab_rails[prevent_ldap_sign_in] false###! **remember to close this block with EOS below** gitlab_rails[ldap_servers] YAML.load <<-EOSmain:label: LDAPhost: 172.16.10.180port:…

C++ | Leetcode C++题解之第398题随机数索引

题目&#xff1a; 题解&#xff1a; class Solution {vector<int> &nums; public:Solution(vector<int> &nums) : nums(nums) {}int pick(int target) {int ans;for (int i 0, cnt 0; i < nums.size(); i) {if (nums[i] target) {cnt; // 第 cnt 次…

c++中的继承和多态

目录 Linux中的管道通信 ​编辑派生类的默认成员函数 继承 派生类的构造 隐藏 如何设计一个不能被继承的类 菱形继承 virtual virtual是如何解决的 内存对象模型 继承和组合 继承 组合 多态 概念 多态的构成条件 虚函数的重写 Linux中的管道通信 派生类的默认成…

ESKF学习笔记

参考资料 https://zhuanlan.zhihu.com/p/441182819 惯性导航(三)-基于流型的ESKF及代码实现_eskf和ekf-CSDN博客 用ESKF实现IMU/GNSS组合导航&#xff08;学习记录&#xff09;_eskf imu-CSDN博客 0.ESKF与KF的区别以及总体流程 0.1卡尔曼滤波过程 卡尔曼滤波的流程按照1…

unity安装配置和vs2022联动教程

目录 1.选择vs2022配置 2.安装unity 2.1安装unity hub 2.2注册个人账号 2.3安装编辑器 2.4修改为简体中文 2.5添加许可证 2.6安装位置修改 3.项目的创建 3.1如何创建 3.2如何选择 3.3配置语言 3.4去哪里找语言包 4.unity编辑器窗口的介绍 4.1游戏的运行和停止 4…

Leetcode面试经典150题-162.寻找峰值

解法都在代码里&#xff0c;不懂就留言或者私信 想清楚的话会特别简单&#xff0c;你可能想不到这是个二分。。。 class Solution {/**本题题目规定我们只能用O(logN)的时间复杂度来解题&#xff0c;这显然就是让二分嘛而题目给的数组本身是无需&#xff0c;怎么二分呢其实我…