资讯专栏INFORMATION COLUMN

帮你读懂preact源码(三)

yuanxin / 3476人阅读

摘要:对回收的处理在中,回收调用了两个方法,节点的回收一般会调用,组件的回收会调用。个人理解从以上源码阅读中我们可以看到,最大的性能问题在于递归的,中的与也是为了缓解这个问题。为不同类型的更新分配优先级。

对回收的处理

在preact中,回收调用了两个方法,dom节点的回收一般会调用recollectNodeTree,组件的回收会调用unmountComponent。

preact复用dom的秘密在于当要卸载一个组件的时候,只有组件的根节点会从父节点上卸载掉,组件完整的dom仍然存在,被卸载的组件会保存在components对象中。

在创建组件的时候又通过nodeName拿到对应的dom节点树,挂载在组件实例的inst.nextBase上,在renderComponent的时候,再diff nextBase与新的虚拟dom树rendered。

相关主要代码如下:

function createComponent(Ctor, props, context) {
    let list = components[Ctor.name],
        inst;

    if (Ctor.prototype && Ctor.prototype.render) {
        inst = new Ctor(props, context);
        Component.call(inst, props, context); 
    } else { // 对应函数组件
        inst = new Component(props, context);
        inst.constructor = Ctor;
        inst.render = doRender;
    }

    if (list) {
        for (let i = list.length; i--;) {
            if (list[i].constructor === Ctor) {
                inst.nextBase = list[i].nextBase;
                list.splice(i, 1);
                break;
            }
        }
    }

    return inst;
}
setState的处理

更改组件上的state,然后将要渲染的组件放在一个数组中,在下一次event loop的时候渲染:

    setState: function setState(state, callback) {
        let s = this.state;
        if (!this.prevState) this.prevState = extend({}, s);
        extend(s, typeof state === "function" ? state(s, this.props) : state);
        if (callback)(this._renderCallbacks = this._renderCallbacks || []).push(callback);
        enqueueRender(this);
    },

    function enqueueRender(component) {
        // component._dirty为false且items原本为空数组就能渲染
        if (!component._dirty && (component._dirty = true) && items.push(component) == 1) {
            (options.debounceRendering || defer)(rerender); //异步的执行render,要执行render方法的component中的_dirty设为true
        }
    },

    function rerender() {
        let p,
            list = items;
        items = [];
        while (p = list.pop()) {
            if (p._dirty) renderComponent(p);
        }
    }
preact对事件的处理

preact为了减少增减事件对性能和内存的影响,当为dom做事件监听时,添加的是一个代理函数。

function setAccessor(node, name, old, value, isSvg) {
// ...
if (name[0] == "o" && name[1] == "n") {
            let useCapture = name !== (name = name.replace(/Capture$/, ""));
            name = name.toLowerCase().substring(2);
            if (value) {
                if (!old) node.addEventListener(name, eventProxy, useCapture);
            } else {
                node.removeEventListener(name, eventProxy, useCapture);
            }
            (node._listeners || (node._listeners = {}))[name] = value; 
        } 
// ...
}
    function eventProxy(e) {
        return this._listeners[e.type](options.event && options.event(e) || e);
    }
fiber(个人理解)

从以上源码阅读中我们可以看到,react最大的性能问题在于递归的diff,react中的shouldCompnentUpdate与PureComponent也是为了缓解这个问题。但是当应用比较大的时候一个高级别组件的diff还是很容易使得动画掉帧。

fiber的出现就是为了解决这个问题,react fiber将计算工作分成了多个小片,这使得整个计算工作可以暂停,中止,或重新开始。为不同类型的更新分配优先级。当动画或用户交互触发时,就可以先暂停低优先级的更新工作,保证动画的流畅性,等所有的渲染计算工作完成,对dom更新进行一次commit。

参考

https://calendar.perfplanet.c...

https://reactjs.org/docs/impl...

https://segmentfault.com/a/11...

https://www.w3ctech.com/topic...

https://zhuanlan.zhihu.com/p/...

https://github.com/acdlite/re...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/95133.html

相关文章

  • 帮你读懂preact源码(一)

    摘要:是一个最小的库,但由于其对尺寸的追求,它的很多代码可读性比较差,市面上也很少有全面且详细介绍的文章,本篇文章希望能帮助你学习的源码。建议与源码一起阅读本文。 作为一名前端,我们需要深入学习react的运行机制,但是react源码量已经相当庞大,从学习的角度,性价比不高,所以学习一个react mini库是一个深入学习react的一个不错的方法。 preact是一个最小的react mi...

    XboxYan 评论0 收藏0
  • 帮你读懂preact源码(二)

    摘要:最后删除新的树中不存在的节点。而中会记录对其做了相应的优化,节点的的情况下,不做移动操作。这种情况,在中得到了优化,通过四个指针,在每次循环中先处理特殊情况,并通过缩小指针范围,获得性能上的提升。 上篇文章已经介绍过idff的处理逻辑主要分为三块,处理textNode,element及component,但具体怎么处理component还没有详细介绍,接下来讲一下preact是如何处理...

    Warren 评论0 收藏0
  • 数据结构与算法():带你读懂选择排序(Selection sort)

    摘要:基本介绍选择式排序也属于内部排序法,是从欲排序的数据中,按指定的规则选出某一元素,再依规定交换位置后达到排序的目的。而移动次数与序列的初始排序有关。空间复杂度简单选择排序需要占用个临时空间,在交换数值时使用。 showImg(https://img-blog.csdnimg.cn/20190509221741422.gif); showImg(https://segmentfault....

    Kaede 评论0 收藏0
  • 源码入手,一文带你读懂Spring AOP面向切面编程

    摘要:,,面向切面编程。,切点,切面匹配连接点的点,一般与切点表达式相关,就是切面如何切点。例子中,注解就是切点表达式,匹配对应的连接点,通知,指在切面的某个特定的连接点上执行的动作。,织入,将作用在的过程。因为源码都是英文写的。 之前《零基础带你看Spring源码——IOC控制反转》详细讲了Spring容器的初始化和加载的原理,后面《你真的完全了解Java动态代理吗?看这篇就够了》介绍了下...

    wawor4827 评论0 收藏0
  • 少啰嗦!一分钟带你读懂Java的NIO和经典IO的区别

    摘要:的选择器允许单个线程监视多个输入通道。一旦执行的线程已经超过读取代码中的某个数据片段,该线程就不会在数据中向后移动通常不会。 1、引言 很多初涉网络编程的程序员,在研究Java NIO(即异步IO)和经典IO(也就是常说的阻塞式IO)的API时,很快就会发现一个问题:我什么时候应该使用经典IO,什么时候应该使用NIO? 在本文中,将尝试用简明扼要的文字,阐明Java NIO和经典IO之...

    Meils 评论0 收藏0

发表评论

0条评论

yuanxin

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<