黄qiong

2017-09-06 09:54

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

本文作者:IMWeb 黄qiong 原文出处:IMWeb社区 未经同意,禁止转载

用过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是如何渲染的。

1条评论

    您需要 注册 一个IMWeb账号或者 才能进行评论。