Preact 源码解析系列一 :简单DOM渲染

用过Preact的人都知道,在每个JS文件之前,都需要写 /** @jsx h */,那这句话是什么作用呢?

先从JSX说起。

解析与编译

JSX实际上就是一个语法糖,让我们能以下简单明了的形式来定义UI长什么样,但是它需要编译器帮助编译,这里使用Babel。

Before:

/** @jsx h */
let foo = <div id="foo">Hello!</div>;

/** @jsx h */ 这句话就是为了告诉编辑器,每个解析后的jsx节点都需要调用h这个函数解析。

经过Babel编译之后就变成了,

var foo = h('div', {id:"foo"}, 'Hello!');

h()

接下来看看 h 做了一件什么事,它其实相当于react.createElement(),用于创建虚拟DOM,而所谓的虚拟DOM实际上就是一个JS对象,它长这样

{
    nodeName: "div",
    attributes: {'id': 'foo'},
    children: ['hello']
}

我们给它一个类装着

function Vnode(nodeName, attributes, children) {
  this.nodeName =  nodeName;
  this.attributes = attributes;
  this.children = children;
}

其实h的源码很简单,就是把后面的child做成数组,给children,返回一个虚拟DOM对象。

function h(nodeName, attributes, ...args) {  
      let children = args.length ? [].concat(...args) : null;
      return new Vnode( nodeName, attributes, children );
}

接着在render()函数里将其渲染成一个真实的DOM

Preact = {
    render: function(vNode, container) {
        // 当其类型为string的时候,返回一个文本节点
        if(typeof vNode === 'string') {
            return document.createTextNode(vNode);
        }
        let{nodeName, attributes: attrs, children} = vNode;
        let node;
        if(typeof nodeName === 'string') {
            node = document.createElement(nodeName);

            if(attrs) {
            // 用for in 来遍历对象的时候,使用hasOwnProperty可以很好的避免来自原型对象扩展所带来的困扰
                for(let key in attrs) {
                    if(attrs.hasOwnProperty(key)) {
                        node.setAttribute(key, attrs[key]);
                    }
                }
            }
            (children || []).forEach(function(child){
               //使用递归渲染
                node.appendChild(Preact.render(child))
            })       
        }
        container.appendChild(node)
    }
}

接着,调用render这个方法就可以渲染出想要的结果

Preact.render(h('div',{id: 'daisy'},'hello world'), document.getElementById('container'))

接着我们就可以在浏览器里看到hello world啦

总结一下: 其实Preact的渲染的还是很简单的,整个过程就是

1、Babel解析JSX

2、h函数将解析后JSX节点转成虚拟DOM

3、render函数把它转成真实的节点

当然这个例子还很简单,对于render的element只考虑到了string跟浏览器的已有元素(div)的情况,但是没有考虑到渲染component的情况,同时也没有diff算法加快渲染。

下一节,来看渲染自定义的component是如何渲染的。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大内老A

WCF技术剖析之十九:深度剖析消息编码(Encoding)实现(上篇)

消息作为WCF进行通信的唯一媒介,最终需要通过写入传输层进行传递。而对消息进行传输的一个前提或者是一项必不可少的工作是对消息进行相应的编码。WCF提供了一系列可...

1666
来自专栏漏斗社区

Java代码审计| Spring框架知识篇

在上期的Java代码审计Spring框架思路篇中,斗哥为大家讲述了如何得到Spring审计的Demo,审计源码,根据Spring框架审计思路初步判定是否存在漏洞...

642
来自专栏flutter开发者

[Flutter Widget]Tooltip

在前面的文章中我们讲到了Wrap的用法,介绍了Flutter中的流式布局,在文章的最后让大家实现如下效果:

1005
来自专栏全沾开发(huā)

FlowType简易入门指北

FlowType简易入门指北 写了一段时间JavaScript了,作为一个弱类型语言,无视类型判断在开发过程中带来了很多的好处,in...

4196
来自专栏Windows Community

New UWP Community Toolkit - RotatorTile

概述 UWP Community Toolkit  中有一个为图片或磁贴提供轮播效果的控件 - RotatorTile,本篇我们结合代码详细讲解  Rotato...

2898
来自专栏QQ会员技术团队的专栏

gulp源码解析(一)—— Stream详解

作为前端,我们常常会和 Stream 有着频繁的接触。比如使用 gulp 对项目进行构建的时候,我们会使用 gulp.src 接口将匹配到的文件转为 strea...

1985
来自专栏QQ会员技术团队的专栏

gulp 源码解析(一):Stream 详解

作为前端,我们常常会和 Stream 有着频繁的接触。比如使用 gulp 对项目进行构建的时候,我们会使用 gulp.src 接口将匹配到的文件转为 strea...

2070
来自专栏一个会写诗的程序员的博客

TypeScript 之父简介:TS Anders Hejlsberg: Introducing TypeScript参考资料TypeScript入门指南(JavaScript的超集)

https://channel9.msdn.com/posts/Anders-Hejlsberg-Introducing-TypeScript

732
来自专栏码农阿宇

利用Asp.Net Core的MiddleWare思想处理复杂业务流程

最近利用Asp.Net Core 的MiddleWare思想对公司的古老代码进行重构,在这里把我的设计思路分享出来,希望对大家处理复杂的流程业务能有所帮助。

672
来自专栏函数式编程语言及工具

FunDA(13)- 示范:用户自定义操作函数 - user defined tasks

   FunDA是一种函数式的编程工具,它所产生的程序是由许多功能单一的细小函数组合而成,这些函数就是用户自定义操作函数了。我们在前面曾经提过FunDA的运作原...

1738

扫码关注云+社区