歪门邪道性能优化:魔改三方库源码,性能提高几十倍! (7)

我们发现Scripting下降到了3.2秒左右,比之前减少约800毫秒,而mousedown的时间也从之前的几百毫秒下降到了50毫秒,在图上几乎都看不到了,mouseup事件也不怎么看得到了,又算进了一步吧~

忍痛阉割功能

到目前为止,我们的性能优化都没有阉割功能,响应速度从7.5秒下降到了3秒多一点,优化差不多一倍。但是,目前这速度还是要三秒多,别说作为一个工程师了,作为一个用户我都忍不了。咋办呢?我们是真的有点黔驴技穷了。。。

看看上面那个性能图,主要消耗时间的有两个,一个是click事件,还有个timer。timer到现在我还不知道他哪里来的,但是click事件我们是知道的,就是用户点击某个事件后,更改SelectContext的selected属性,然后selected属性从顶层节点传入触发下面组件的更新,中间儿子节点通过shouldComponentUpdate跳过更新,孙子节点直接连接SelectContext获取selected属性更新自己的状态。这个流程是我们前面优化过的,但是,等等,这个貌似还有点问题。

在我们的场景中,中间儿子节点其实包含了高达7000个背景格子,虽然我们通过shouldComponentUpdate跳过了render的执行,但是7000个shouldComponentUpdate本省执行也是需要时间的啊!有没有办法连shouldComponentUpdate的执行也跳过呢?这貌似是个新的思路,但是经过我们的讨论,发现没办法在保持功能的情况下做到,但是可以适度阉割一个功能就可以做到,那阉割的功能是哪个呢?那就是暴露给外部的受控selected属性!

前面我们提到过选中一个事件有两个途径:

用户通过点击某个事件来改变selected的值

开发者可以在外部直接修改selected的值来选中某个事件

之所以selected要放在顶层组件上就是为了实现第二个功能,让外部开发者可以通过这个受控的selected属性来改变选中的事件。但是经过我们评估,外部修改selected这个并不是我们的需求,我们的需求都是用户点击来选中,也就是说外部修改selected这个功能我们可以不要。

如果不要这个功能那就有得玩了,selected完全不用放在顶层了,只需要放在事件外层的容器上就行,这样,改变selected值只会触发事件的更新,啥背景格子的更新压根就不会触发,那怎么改呢?在我们前面的Calendar -- Background -- Event模型上再加一层EventContainer,变成Calendar -- Background -- EventContainer -- Event。SelectContext.Provider也不用包裹Calendar了,直接包裹EventContainer就行。代码大概是这个样子:

// Calendar.js // Calendar简单了,不用接受selected参数,也不用SelectContext.Provider包裹了 class Calendar extends Component { render() { return ( <Background /> ) } } // Background.js // Background要不要使用shouldComponentUpdate阻止更新可以看看还有没有其他参数变化,因为selected已经从顶层拿掉了 // 改变selected本来就不会触发Background更新 // Background不再渲染单个事件,而是渲染EventContainer class Background extends PureComponent { render() { const { events } = this.props; return ( <div> <div>这里面是7000个背景格子</div> 下面是渲染1400个事件 <EventContainer events={events}/> </div> ) } } // EventContainer.js // EventContainer需要SelectContext.Provider包裹 // 代码类似之前的Calendar import SelectContext from './SelectContext'; class EventContainer extends Component { constructor(...args) { super(...args) this.state = { selected: null }; this.setSelected = this.setSelected.bind(this); } setSelected(selected) { this.setState({ selected }) } render() { const { selected } = this.state; const { events } = this.props; const value = { selected, setSelected: this.setSelected } return ( <SelectContext.Provider value={value}> {events.map(event => <Event event={event}/>)} </SelectContext.Provider> ) } } // Event.js // Event跟之前是一样的,从Context中取selected来决定自己的渲染样式 import SelectContext from './SelectContext'; class Event extends Component { render() { const { selected, setSelected } = this.context; const { event } = this.props; return ( <div className={ selected === event ? 'class1' : 'class2'} onClick={() => setSelected(event)}> </div> ) } } Event.contextType = SelectContext; // 连接Context

这种结构最大的变化就是当selected变化的时候,更新的节点是EventContainer,而不是顶层Calendar,这样就不会触发Calendar下其他节点的更新。缺点就是Calendar无法从外部接收selected了。

需要注意一点是,如果像我们这样EventContainer下面直接渲染Event列表,selected不用Context也可以,可以直接作为EventContainer的state。但是如果EventContainer和Event中间还有层级,需要穿透传递,仍然需要Context,中间层级和以前的类似,使用shouldComponentUpdate阻止更新

还有一点,因为selected不在顶层了,所以selected更新也不会触发中间Background更新了,所以Background上的shouldComponentUpdate也可以删掉了。

我们这样优化后,性能又提升了:

image-20210120161336248

现在Scripting时间直接从3.2秒降到了800毫秒,其中click事件只有163毫秒,现在从我使用来看,卡顿已经不明显了,直接录个动图来对比下吧:

Jan-20-2021 16-42-53

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsssgj.html