试试这么读 preact 源码(二)- render

363 阅读2分钟

旧版本中的 render 方法只做了一件事,就是调起 diff 方法,新版本中的 render 引入了 Fragment ,让我们写的自定义组件都成了子组件

React.Fragment

Fragment 就是让 react 组件能够聚合一个个子元素列表,那样就不必要在增加一个额外的节点了

// 旧的写法
render(){
    return (
        <div>
          <ChildA />
          <ChildB />
          <ChildC />
        </div>
    )
}

// Fragment
render(){
    return (
        <>
          <ChildA />
          <ChildB />
          <ChildC />
        </>
    )
}

<></> 其实是 <React.Fragment/> 的语法糖,当组件中需要遍历数组传递 key 值时,就需要使用 <React.Fragment/>


render(){
    return (
        <div>
            {
                goodsList.map(item => (
                    <React.Fragment key={item.id}>
                        <p>{item.goodsName}</p>
                    </React.Fragment>
                ))
            }
        </div>
    )
}

了解了 reactFragment ,那 preact 是如何使用和实现的呢:

使用:

import {h, render, Component, Fragment} from 'preact';

class App extends Component {
    render(){
        return (
            <Fragment>
                <div>preact</div>
                <div>使用了Fragment</div>
            </Fragment>
        )
    }
}

preact 中,并没有类似 <></> 这样的语法糖,下面来看下它的实现原理:


function Fragment() {}

function render(vnode, parentDom, replaceNode) {
    // ...
    vnode = createElement(Fragment, null, [vnode]);
    // ...
}

render 方法中调用了 createElement 方法创建了一个虚拟 dom ,第一个参数就是 Fragment ,实参是一个 functioncreateElement 这个函数的第一个形参 type 是这个虚拟 dom 的类型,它有可能是 div,span 等,也有可能是一个 function,当讲到 diff 时,会专门有一个分支就是判断 type === Fragment 的情况,那个时候再细讲,这里只简单说下结果:

Fragment 作为虚拟dom的类型传入,在diff 一个组件时,时如果遇到 type === Fragment 时,就直接 diff <Fragment></Fragment> 包裹的子节点,而这些子节点所渲染的真实 dom 会直接挂载在 <Fragment></Fragment> 所对应的父节点上。

以上就是 Fragment 的简单实现,在 render 方法中,通过 createElement 方法重新创建的 vnode 会传入 diffChildren 方法中,这个方法是整个新版中最核心的方法,将在后面专门会说这个方法,这里可以简单的理解为--对比这个 vnode 下的子节点是否更新。

在这个 render 方法中,由于最外层包裹的是一个 Fragment ,所以,这里的 diffChildren 比对的对象就是 Fragment 包裹下的子节点。

下一章,我们就来看看这个 diffChildren 方法。

原文地址