帮你读懂preact源码(三)
对回收的处理
在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。
参考
相关推荐
瓜牛呱呱 2020-11-12
柳木木的IT 2020-11-04
yifouhu 2020-11-02
lei0 2020-11-02
源码zanqunet 2020-10-28
源码zanqunet 2020-10-26
一叶梧桐 2020-10-14
码代码的陈同学 2020-10-14
lukezhong 2020-10-14
lzzyok 2020-10-10
anchongnanzi 2020-09-21
clh0 2020-09-18
changcongying 2020-09-17
星辰大海的路上 2020-09-13
abfdada 2020-08-26
mzy000 2020-08-24
shenlanse 2020-08-18
zhujiangtaotaise 2020-08-18
xiemanR 2020-08-17