最近在努力学习react中,各种工具概念应接不暇:react-redux
、dva
、umi
、redux-saga
、ant design pro
…每一个都需要耗费时间去接触熟悉。上个月常刷的leetCode
刷到中等难度题也开始屡屡碰壁,果然罗马不是一天建成的,还是要不断跳出舒适区,不断折腾才能前进。
引入场景
平时在开发中,少不了前端对服务器传来的数据进行处理整合,然后丢给框架去render
。react
配合redux
开发时,外层容器组件的props
改变时,都会调用render()
去diff
渲染。假如我们在render()
内进行了复杂的数据处理:
1 | import React, { PureComponent } from 'react'; |
每一次render()
都会去过滤一次数据,假如我们这个组件的title
经常变化,但是list
数据不变,就会消耗大量计算时间,导致性能问题。
而在实际的开发中,数据结构往往更加复杂,有时候甚至会有多次的循环。有时候组件的更新并不是因为从服务器拿来的这一段数据结构发生变化造成的(组件中的其他部分更新造成的),但是这一段很重的逻辑因为是写在 render 中的,所以不可避免的在每次 render 会调用一次。如果这段逻辑在两次调用的时候,输入参数是一样的,那么输出结果必然一样,所以再次计算是一种十分浪费资源的行为。
接下来我们使用记忆化技术
来优化。
记忆化库-memoize-one
根据memoize-one
名字中的one
可以知道,这个库的每个实例都缓存了一个结果,下一次不同的结果将覆盖上一次的。虽然只能缓存一个数据,但是用到合适的地方却能发挥很大的作用。
使用npm
安装:$ npm install memoize-one
,先看一下官方例子:
1 | import memoizeOne from 'memoize-one'; |
memoizeOne(resultFn, isEqual)
接收一个结果函数和一个对比函数,对比函数为空则默认使用===
来进行入参的比较。
简单来讲就是,memoizeOne()
在原来resultFn()
函数外面包了一层,返回一个函数,然后每次调用的时候看新入参newArgs
是否和上一次的入参lastArgs
一致,参数不变,则直接返回缓存的结果,否则重新执行resultFn(newArgs)
,缓存新结果。
简单改造一下上面的例子:
1 | import React, { Component } from 'react'; |
这样就简单的实现了一个记忆优化。
源码解读
memoize-one
记忆库巧妙的使用了闭包
来实现,一般我是不看源码的,但是这个库的源码只有不到40行的代码,简单易懂,这里就简单看一下:
1 | //isEqual比较函数,用来判断参数是否一致,默认使用全等来判断 |
源码还是比较容易看懂的。
isEqual函数
因为对相等的理解,不同场景不一样,而且参数有时候是复杂的对象,所以我们不能仅仅通过比较操作符 ==
或者 ===
来判断。memoize-one
允许用户自定义传入判断是否相等的函数,比如我们可以使用 lodash 的 isEqual 来判断两次参数是否相等。
1 | import memoizeOne from 'memoize-one'; |