Vue源码分析(Render渲染函数)

2023-11-17

Render函数

渲染DOM原理

在前面的内容我们学习到了Vue的响应式,但是只有响应式是不够的,我们要将内容渲染出来。Vue的templates就是通过Render(渲染)函数渲染出来的。

第一阶段(生成虚拟DOM)

在Vue中,如果你把模版直接传入Vue实例,那Vue会执行完整的编译,把传入的template编译为浏览器可运行的DOM,就像你直接在DOM中编写模板一样。

如果你使用vue-cli构建项目,会用到webpack和vue-loader。它会在构建时预编译模板为可以直接解析的DOM代码(h()函数),即纯JavaScript代码。还有另一种编译模式,就是将编译器也打包进去,压缩之后会比第一种体积大一些。

上面两种情况其实都是使用Render函数生成虚拟DOM

第二阶段(生成真实DOM)

Vue会基于第一阶段的虚拟DOM转换成真实DOM。

虚拟DOM更新

第一、二阶段相当于完成了初始化,如果之后DOM需要更新呢?回顾之前讲的autorun函数(用于订阅发布),我们可以将虚拟DOM的代码放在里面,当数据改变时,Render函数会生成新的虚拟DOM,即触发我们的发布,新的虚拟DOM和旧的虚拟DOM进行比较,得出最少需要更新的节点并生成真实DOM完成一次更新。

虚拟DOM

简单来说,就是将真实的DOM转化成JS代码,既然都是JS代码,虚拟DOM产生变化的时候也只需要修改JS的一些内容,最终再转换成真实DOM。

你可能会好奇,我本来直接操作DOM就可以,为什么要在中间加一系列操作呢?

这是由于操作真实DOM中会存在一些问题,当DOM节点非常多的时候:会有资源消耗问题和执行效率问题。

使用JavaScript操作真实DOM会非常消耗资源,因为要修改真实DOM操作的内容很多,但是如果使用虚拟DOM,你无论是如何增删修改节点,都只是在操作JS,这样会很节省资源。同样,这样只操作JS,用JS计算差异,也会比真实DOM比较差异快很多。(当然:如果你能将DOM操作到炉火纯青,保证每次对DOM的操作都是较为节约快捷的方式,那还是要比虚拟DOM快的,毕竟我们多了一步将它转换成了JS,又变回真实DOM的过程)

<div>Hello World</div>

调用Render函数转化为虚拟DOM

import { openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createElementBlock("div", null, "Hello World"))
}

Vue整体机制

现在我们来整合一下之前的所有知识

每个组件都有一个渲染函数,它包装在我们第一节实现的autorun函数中,当执行渲染的时候,getters收集它的依赖。同时,每个组件都有一个watcher观察者去监听,一旦数据更新或依赖的渲染属性发生变化,我们就执行收集的依赖(即渲染函数),这样每个组件即可实现自己的自动循环渲染。

请添加图片描述

Render Function API

export default{
	render(h){
		return h('div',{},[...])
	}
}

上面是渲染函数的使用,你需要一个参数h,h是一种简写表示超脚本(HyperScript),这是一种虚拟DOM渲染函数的编写风格。就像超文本叫HTML一样,他没有什么特殊的含义,只是方便书写的表现形式而已。

接下来我们分析一下h()

h()接收三个函数,第一个是元素类型;第二个是参数对象();第三个是子节点

h('div','some text')
h('div',{class:'foo'},'some text')
h('div',{...},[
	'some text',
	h('span','bar')
])

最后一个的渲染效果就像

<div ...>some text <span> bar </span></div>

h()同样也接受一个组件定义,这将会创建一个组件实例

import MyComponent from '...'
h(MyComponent,{
	props:{...}
})

练习Render函数动态渲染标签

前面说了这么多,让我们来做一个demo真正体验一下

传入tags数组

<example :tags="['h1', 'h2', 'h3']"></example>

渲染出标签并显示元素索引值

<div>
  <h1>0</h1>
  <h2>1</h2>
  <h3>2</h3>
</div>

如果经常使用Vue,你的第一反应可能是使用一个构造器,动态渲染

<component is="h1"></component>

但是这个组件设计的初衷是用来在组件间动态切换,而不是渲染真实的标签;如果Vue没有将这个api提供给我们,我们的开发就会受限。这也是模板的一个缺点,不如jsx灵活。开发者总不能等待vue官方提供解决办法,所以在这里我们使用渲染函数

<script src="../node_modules/vue/dist/vue.js"></script>

<div id="app">
  <example :tags="['h1', 'h2', 'h3']"></example>
</div>

<script>
  Vue.component('example', {
    props: ['tags'],
    render(h) {
      return h('div', {
        attrs: { id: 'hello' }
      }, this.tags.map((tag, i) => h(tag, i)))
    },
  })

  new Vue({ el: '#app' })
</script>

我们通过example创建的模板就是这样

<div id="hello">
	<h1>0</h1>
	<h2>1</h2>
	<h3>2</h3>
</div>
函数组件和状态组件

上面我们使用的是Vue的状态组件,而函数组件不包含state和props,你可以理解它就是一个函数。

const foo = {
	functional: true,
	render: h => h('div','foo')
}

函数组件特点:

  1. 组件不支持实例化。
  2. 优化更优,因为在Vue中它的渲染函数比父级组件更早被调用,但是他并不会占用很多资源,因为它没有保存数据和属性,所以它常用于优化一个有很多节点的组件。
  3. 容易扩展,如果你的组件只是用来接收 prop然后显示数据,或者一个没有状态的按钮,建议使用函数组件。
  4. 函数组件没有this,获取prop可以通过render函数的第二参数得到render(h, context)

我们改写一下上面的demo

Vue.component('example', {
  functional: true,
  props: {
    tags: {
      type: Array,
      validator (arr) { return !!arr.length }
    }
  },
  render: (h, context) => {
    const tags = context.props.tags
    return h('div', context.data, tags.map((tag, index) => h(tag, index)))
  }
})

new Vue({ el: '#app' })

练习Render函数动态渲染组件

渲染函数除了可以渲染普通标签外,还可以渲染组件,下面代码有FooBar组件,点击toggle按钮的时候,切换两组件的显示状态。

<script src="../node_modules/vue/dist/vue.js"></script>

<div id="app">
  <example :ok="ok"></example>
  <button @click="ok = !ok"></button>
</div>

<script>
const Foo = {
  functional: true,
  render: h => h('div', 'foo')
}

const Bar = {
  functional: true,
  render: h => h('div', 'bar')
}

Vue.component('example', {
  functional: true,
  props: {
    ok: Boolean
  },
  render: (h, context) => h(context.props.ok ? Foo : Bar)
})

new Vue({
  el: '#app',
  data: {
    ok: true
  }
})
</script>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Vue源码分析(Render渲染函数) 的相关文章

随机推荐

  • CentOS7安装部署wordpress

    环境介绍 CentOS7 3安装部署wordpress 本环境主机运行在阿里云 部署方式为单节点部署 主机配置 系统 CentOS7 3 mini CPU 1核 内存 1GB 硬盘 60G 外网带宽 1Mbs 一 配置主机名 root lo
  • mysql explain ref const_MySQL EXPLAIN 详解

    一 介绍 EXPLAIN 命令用于SQL语句的查询执行计划 这条命令的输出结果能够让我们了解MySQL 优化器是如何执行SQL 语句的 这条命令并没有提供任何调整建议 但它能够提供重要的信息帮助你做出调优决策 先解析一条sql语句 你可以看
  • pytorch 实现LSTM

    Pytorch基础知识点整理 梯度 下降 coding utf 8 from math import pi import torch import torch optim x torch tensor pi 3 pi 6 requires
  • js数组不同类型元素去重

  • Ubuntu 16.04 服务器安装 hadoop3.1.2

    1 事前准备 1 相关配置信息 仅供参考 ubuntu版本为16 04服务器版 服务器配置为1核2GB hadoop版本我选择hadoop3 1 2 2 相关资源获取 服务器可选择国内的腾讯云和阿里云 也可以选择华为云 任君喜好 系统的话在
  • 校园社区app

    此项目是面向在校大学生开发的一个集预约购物 组织活动 实事热帖于一体的社区app 前后台交互数据采用的是json数据格式 网络请求采用的是volley 后台采用mysql数据库 如果有写的不好的地方还望大家指正 主要功能为 预约购物 组织活
  • DataGrip 2022.2.2 Unknown column ‘generation_expression‘ in ‘field list‘

    安装DataGrip 2022 2 2 连接本地数据库 编写脚本时发现无法自动提示数据库表字段 搜索结果 都是在连接属性 Options Introspection using JDBC metadata 在此版本没有发现此选项 查找发现在
  • undefined、null、isNan

    null 和 undefined的区别 在用法上几乎没有区别 if undefined console log undefined is false undefined is false if null console log null i
  • [电动智能汽车-7]:汽车CAN总线详解

    目录 第1章 CAN总线概述 1 1 概述 1 2 发展历史 1 3 标准化 1 4 底层标准 1 5 上层标准 1 6 总线特点 1 7 CAN总线分类 1 8 应用 1 9 局限性 第2章 电路基础 2 1 硬件拓扑 2 2 收发器 2
  • 【微信小程序】小程序点击图片放大(图片预览)

    这个强大的API wx previewImage 嘿嘿嘿 接下来我们来讲一下微信小程序中图片点击放大预览的实现步骤 思路 1 点击事件 2 放大 3 左右滑动查看上 下一张 在绑定点击事件的时候我们需要同时获取到点击图片的url和这一组数据
  • linux 信号传递参数,Qt 信号槽如何传递参数(或带参数的信号槽)

    信号槽如何传递参数 或带参数的信号槽 利用Qt进行程序开发时 有时需要信号槽来完成参数传递 带参数的信号槽在使用时 有几点需要注意的地方 下面结合实例进行介绍 第一点 当信号与槽函数的参数数量相同时 它们参数类型要完全一致 signals
  • 不死神兔详细解释及解题,看不懂算我输

    不死神兔 不死神兔也叫做斐波那契数列或者叫做黄金分割数列或者叫做兔子数列 不死神兔问题 有1对兔子 从出生后的第3个月起每个月都生一对兔子 小兔子长到第三个月后每个月 又生一对兔子 假如兔子都不死 问第n个月有几对兔子 需求 我们这里需要知
  • JDBC工具类封装

    jdbc工具类的封装 package com hyc study02 utils import java io InputStream import java sql import java util Properties public c
  • 数字化和物联网的发展如何改变我们的生活方式?

    数字化和物联网 IoT 的发展已经给我们的生活方式带来了重大变化 而且这些变革将在未来继续发生 以下是数字化和物联网影响我们生活的一些方式 连接设备和智能家居 物联网使日常物品能够连接到互联网并相互通信 这种连通性允许创建智能家居 其中可以
  • SpringBoot学习---yaml配置

    本篇博客目录 一 什么是yaml 二 yaml基础语法与数据类型 1 yaml基础语法 2 数据类型 1 对象类型 2 数组类型 3 复合结构 4 纯量类型 5 引用 三 yaml注入配置文件 1 yaml配置注入到实体类 2 yaml加载
  • Linux 维护日志:今日系统宕机,问题记录

    查看日志 显示包含如下信息 Jul 21 10 55 10 EYKERP1 kernel sd 3 0 0 0 SCSI error return code 0x00010000 Jul 21 10 55 10 EYKERP1 kernel
  • Redis基础知识(三):缓存穿透、缓存击穿、缓存雪崩

    文章目录 一 缓存穿透 出现过程 解决方法 二 缓存击穿 出现过程 解决方法 三 缓存雪崩 出现过程 解决方法 我们在项目中大量使用Redis承接海量数据的冲击 但是使用过程中也会遇到一些特殊的情况 这个就是缓存击穿 缓存穿透 缓存雪崩 一
  • 全志H3-----制作刷机包(TF卡)

    开发环境 Ubuntu 14 04 LTS busybox 1 26 2 U boot kernel lichee 全志原厂提供的BSP 1 制作一个 256M 的空白映象文件 你可以制作得更小或更大 dd if dev zero of f
  • JWT 安全及案例实战

    文章目录 一 JWT json web token 安全 1 Cookie 放在浏览器 2 Session 放在服务器 3 Token 4 JWT json web token 4 1 头部 4 1 1 alg 4 1 2 typ 4 2
  • Vue源码分析(Render渲染函数)

    文章目录 Render函数 渲染DOM原理 虚拟DOM Vue整体机制 Render Function API 练习Render函数动态渲染标签 函数组件和状态组件 练习Render函数动态渲染组件 Render函数 渲染DOM原理 在前面