最近在看《vue.js 设计与实现》,看到了虚拟 DOM 这里,做了个笔记

文章目录

  • 1、VNode虚拟DOM节点
  • 2、render工作原理
  • 3、代码实现
  • 4、render拓展

1、VNode虚拟DOM节点

  • 虚拟DOM节点如下:

    const vnode = {tag: 'h2',props: {class: 'active',data: 'text',onClick: () => alert('Hello Render!')},children: 'Click Me!'
    }
    
    1. tag:用来描述标签名称,所以tag: 'h2'描述的就是一个<h2>标签。
    2. props:是一个对象,用来描述<h2>标签的类名、属性、事件等内容。可以看到,我给h2绑定一个active类名,一个text自定义属性,一个click点击事件。
    3. children:用来描述标签的子节点,在上面的代码中,children是一个字符串值,意思是h2标签有一个文本子节点:<h2>Click Me!</h2>

2、render工作原理

  1. 第一步: 创建元素,把vnode.tag作为标签名称来创建DOM元素。
  2. 第二步: 为元素添加属性和事件,遍历vnode.props对象。如果key是class,说明它是一个类名,将其直接绑定给tag元素;如果key以on字符串开头,说明它是一个事件,把字符on截取掉后再调用toLowerCase函数将事件名称小写化,最终得到合法的事件名称,例如onClick会变成click,最后调用addEventListener绑定事件处理函数。
  3. 第三步: 处理children,如果children是一个数组,就递归地调用render继续渲染。注意,此时我们要把刚刚创建的元素作为挂载节点(父节点);如果children是字符串,则使用createTextNode函数创建一个文本节点,并将其添加到新的创建的元素内。

3、代码实现

  • 自己手写一个简陋的render渲染器,上代码:

    <!DOCTYPE html>
    <html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>.active {color: red;}</style>
    </head><body><!-- 根节点 --><div id="app"></div><script>// 获取根节点const app = document.getElementById("app");// 要进行的挂载的虚拟 dom 节点const vnode = {// 节点类型tag: 'h2',// 节点上面绑定的类型、属性、方法props: {class: 'active', // 类名data: 'text', // 自定义属性onClick: () => alert('Hello Render!') // 方法},// 子节点children: 'Click Me!'}// 封装的渲染器 render 函数// vnode 是虚拟 dom 节点// container 是要挂载节点的元素const render = (vnode, container) => {// 根据 vnode.tag 创建对应的 dom 节点const el = document.createElement(vnode.tag);// 遍历 vnode.props,给节点绑定类名、属性、事件等for (const key in vnode.props) {if (key === 'class') {// 如果是类名,则给元素添加类名el.className += vnode.props[key]} else if (/^on/.test(key)) {// 如果属性是以 on 开头的,那么就绑定对应的事件el.addEventListener(key.substr(2).toLowerCase(), // 改变事件类型:例如 onClick 变为 clickvnode.props[key] // 绑定对应的事件)} else {// 如果是自定义属性,给元素绑定对应的属性el.setAttribute(key, vnode.props[key])}}// 处理子节点if (typeof vnode.children === 'string') {// 如果子节点是 string 类型,那么就是本文节点el.appendChild(document.createTextNode(vnode.children));} else if (Array.isArray(vnode.children)) {// 如果子节点是 array 类型,那么继续渲染子节点vnode.children.forEach(child => render(child, el));}// 将渲染的 vnode 挂载在根节点上app.appendChild(el);}// 调用 render 函数render(vnode, app)</script>
    </body></html>
    
  • 看一下页面效果:

4、render拓展

在上面进行VNode渲染的过程,我只是讨论了当VNode是一个虚拟DOM节点的情况,那么接下来我将会讨论组件component的渲染,而component组件应该有两种情况

  1. component是一个函数;
  2. component是一个对象;
  1. 首先直接来看代码的实现:

    <!DOCTYPE html>
    <html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>.active1 {color: red;}.active2 {color: blue;}.active3 {color: orange;}</style></head><body><!-- 根节点 --><div id="app"></div><script>// 获取根节点const app = document.getElementById("app");// 要进行绑定的 component 组件 —— 返回函数const component1 = function () {return {// 节点类型tag: 'h2',// 节点上面绑定的类型、属性、方法props: {class: 'active2', // 类名data: 'component1', // 自定义属性onClick: () => alert('Hello Component1!') // 方法},// 子节点children: 'Function Component!'}}// 要进行绑定的 component 组件 —— 返回对象const component2 = {// 该组件对象的 render 属性render() {return {// 节点类型tag: 'h2',// 节点上面绑定的类型、属性、方法props: {class: 'active3', // 类名data: 'component2', // 自定义属性onClick: () => alert('Hello Component2!') // 方法},// 子节点children: 'Object Component!'}}}// 要进行的挂载的虚拟 dom 节点(标签元素)const vnode1 = {// 节点类型tag: 'h2',// 节点上面绑定的类型、属性、方法props: {class: 'active1', // 类名data: 'vnode', // 自定义属性onClick: () => alert('Hello Render!') // 方法},// 子节点children: 'Click Me!'}// 要进行的挂载的虚拟 dom 节点(组件1)const vnode2 = {tag: component1}// 要进行的挂载的虚拟 dom 节点(组件2)const vnode3 = {tag: component2}// 挂载标签元素的方法const momentElement = (vnode, container) => {// 根据 vnode.tag 创建对应的 dom 节点const el = document.createElement(vnode.tag)// 遍历 vnode.props,给节点绑定类名、属性、事件等for (const key in vnode.props) {if (key === 'class') {// 如果是类名,则给元素添加类名el.className += vnode.props[key]} else if (/^on/.test(key)) {// 如果属性是以 on 开头的,那么就绑定对应的事件el.addEventListener(key.substr(2).toLowerCase(), // 改变事件类型:例如 onClick 变为 clickvnode.props[key] // 绑定对应的事件)} else {// 如果是自定义属性,给元素绑定对应的属性el.setAttribute(key, vnode.props[key])}}// 处理子节点if (typeof vnode.children === 'string') {// 如果子节点是 string 类型,那么就是本文节点el.appendChild(document.createTextNode(vnode.children))} else if (Array.isArray(vnode.children)) {// 如果子节点是 array 类型,那么继续渲染子节点vnode.children.forEach(child => render(child, el))}// 将渲染的 vnode 挂载在根节点上app.appendChild(el);}// 挂载组件的函数const momentComponent = (vnode, container) => {// 如果组件返回的是函数:调用组件函数,获取组件要渲染的内容(虚拟 DOM)// 如果组件返回的是对象:vnode.tag 是组件对象,调用它的 render 函数得到组件要渲染的内容(虚拟 DOM)const componentTree = typeof vnode.tag === 'function' ? vnode.tag() : vnode.tag.render()// 递归地调用 render 渲染 componentTreerender(componentTree, container)}// 封装的渲染器 render 函数// vnode 是虚拟 dom 节点// container 是要挂载节点的元素const render = (vnode, container) => {if (typeof vnode.tag === 'string') {// 如果 vnode.tag 是 string 类型,则表示该 vnode 是标签元素,调用挂载标签元素的函数momentElement(vnode, container)} else if (typeof vnode.tag === 'object' || typeof vnode.tag === 'function') {// 如果 vnode.tag 是 object 类型,则表示该 vnode 是组件,调用挂载组件的函数momentComponent(vnode, container)}}// 调用 render 函数render(vnode1, app)render(vnode2, app)render(vnode3, app)</script></body></html>
    
  2. 看一下页面效果:

  3. 事项说明:

    1. 组件就是一组DOM元素的封装
    2. 代码中我的注释写的都挺清楚了,一定要注意component组件的类型分为Object和Function两种类型,这两种不同情况的返回值都是不一样的,一定要注意返回值,不要搞混。

渲染器 render相关推荐

  1. MAUI 移植 Xamarin.Forms 自定义渲染器

    简介 众所周知, .NET MAUI使用的是Handler处理程序, 而Xamarin使用的则是Render渲染器模式.尽管MAUI中使用了新的渲染模式, 但是仍然Xamarin中的支持Render渲 ...

  2. 7 c4d r20对win_OC渲染器Octane Render V4.0-RC7-R4 for C4D R15-R19-R20 Win/Mac

    OC渲染器Octane Render V4.0-RC7-R4 for C4D R15-R19-R20 Win/Mac Octane Render 4是世界上第一款和最快的GPU加速,无偏差,物理渲染器 ...

  3. UE4文本渲染器Text Render技巧与支持中文

    UE4文本渲染器Text Render技巧与支持中文 首先来支持中文 在目录中新建Font 修改类型 选择天下第一的思源字体 字号根据字体,选择生成以后能看清的最小 大小 字号太小会模糊,太大占资源 ...

  4. c4d支持mac系统渲染器有哪些_C4D Octane Render渲染器 V4.0-RC7-R4 for C4D R15-R19-R20 Win/Mac...

    C4D Octane Render渲染器 V4.0-RC7-R4 for C4D R15-R19-R20 Win/Mac C4D Octane Render渲染器 V4.0-RC7-R4 for C4 ...

  5. 渲染测试软件 d15,D5 Render(D5 渲染器)1.7.0 正式版发布 | 一款 RTX 实时光线追踪可视化实时渲染引擎,而且是国产软件!...

    长期的 Beta版公测后,D5 渲染器终于正式对外发布!最新的正式版是1.7.0.正式版将由免费版+付费增值服务这样的方式进行对外销售.另外最新还新增了社区免费版(社区免费版功能上有一点限制:无法渲染 ...

  6. SDL2源码分析6:拷贝到渲染器(SDL_RenderCopy())

    ===================================================== SDL源码分析系列文章列表: SDL2源码分析1:初始化(SDL_Init()) SDL2源 ...

  7. vr降噪器英文是什么_CR渲染器和VR渲染器在3d效果图上的区别

    个人倾向于用max2018+VR4.2+CR5.0,安装包可共享. 没有不好用的渲染器,只有不好用的渲染技术.VR更有普遍市场,渲染速度快,CR渲染效果好,尤其灯光效果柔和. 相信不论是大神还是小白, ...

  8. 怎么通过id渲染页面_「快页面」动态配置化页面渲染器原理介绍

    引言 「快页面」是知乎内部一个快速搭建后台管理页面的平台,使用者仅用半小时即可将一个常规复杂度的后台页面开发完成. 「快页面」平台的基石是它的「渲染器」,一个能将 JSON 配置渲染成页面的 Reac ...

  9. 深入学习SAP UI5框架代码系列之二:UI5 控件的渲染器

    这是Jerry 2020年的第79篇文章,也是汪子熙公众号总共第261篇原创文章. 系列目录 (0) SAP UI5应用开发人员了解UI5框架代码的意义 (1) UI5 module懒加载机制 (2) ...

最新文章

  1. java 1%10_Java 操作符与运算符详解
  2. BF法-字符模式匹配
  3. python tqdm 不换行_python tqdm 实现滚动条不上下滚动代码(保持一行内滚动)
  4. CListCtrl使用技巧汇总
  5. 安全测试的目的,发现哪些问题
  6. ML之PPMCC:PPMCC皮尔逊相关系数(Pearson correlation coefficient)、Spearman相关系数的简介、案例应用之详细攻略
  7. 神策数据实战学堂开课,分享行业最佳业务和技术实践
  8. 引导类、扩展类、系统类加载器的使用及演示
  9. 哈达玛变换的应用SATD、SAD等匹配算法
  10. solr.Net课程二 solr5.5之core配置
  11. ElasticSearch学习(五):数据导入之Logstash
  12. Android Studio 4.1一键生成代码Template
  13. Java连接MySQL数据库步骤
  14. 中国共用计算机网,《中国公用计算机互联网国际联网管理办法》
  15. 谈谈订单号和流水号的关系
  16. AC上网行为管理(深信服)
  17. 工业级4g无线路由器_工业级4g无线路由器厂家品牌
  18. 芯片数据分析步骤4 标准化-affy
  19. 多可系统的权限规则详解
  20. 那些酷炫的深度学习网络图怎么画出来的?

热门文章

  1. STM32-中断优先级管理NVIC详解
  2. java集合(详解)
  3. 第三组 Alpha(3/3)
  4. 【无人机】无人机的气象数据采集系统设计(Matlab代码实现)
  5. 常见功能点测试用例归纳总结
  6. 分享创意个性古风剪影喜迎国庆PPT模板
  7. visio2013激活软件,绝对靠谱,我自己试过可以用
  8. 实施顾问试水软件开发,只要1天培训,搭搭云全家桶果真神奇
  9. 图像处理 有损压缩变换-傅里叶变换
  10. 关于AUI框架自学心得