关闭

性能优化小册 - React 搜索优化:防抖、缓存、LRU

时间: 2020-04-13阅读: 333标签: 优化

最近要对 react 项目做重构优化等相关的工作,由于有好长时间没碰 react 了,今天索性把一个基于关键字搜索的 demo 做一下简单优化,在此记录以下。

主要从三个方面进行优化处理:

    1. 减少事件的触发频率 - 对关键字键入进行 debounce 处理
    1. 减少 HTTP 请求 - 对重复的 HTTP 请求进行缓存拦截
    1. 缓存淘汰策略 - 使用 LRU 优化缓存


减少事件的触发频率 - debounce

debounce 旨在时间段内控制事件只在最后一次操作触发。

debounce 原理:是维护一个计时器,在规定的 delay 时间后触发函数,在 delay 时间内再次触发的话,就会取消之前的计时器而重新设置。这样一来,只有最后一次操作能被触发。

下面是 react 中 debounce 优化的代码

...
handler = e => {
  let val = e.target.value;
  if(val) { 
    this.search(val);
  }
  this.setState(() => ({
    value: e.target.value
  }))
}

debounce = (fn, delay) => {
  let timer = null;
  return function(event) {
    timer && clearTimeout(timer);
    event.persist && event.persist() // 保留引用,已备异步阶段访问
    timer = setTimeout(() => {
      fn.call(this, event)
    }, delay) 
  }
}

onChangeHandler = this.debounce(this.handler, 1000)
...
render() {
  return (
    <div>
      <input
        // 这里不能设置成 value
        defaulValue={this.state.value}
        onChange={e => this.onChangeHandler(e)}
        placeholder="试着输入一些文字"
      />
      <div>
        <Suspense fallback="Loading">
          {this.renderMovies}
        </Suspense>
      </div>
    </div>
  );
}

这里需要注意的是:如果想要异步访问合成事件对象 SyntheticEvent,需要调用 persist() 方法或者对事件对象进行深拷贝 const event = { ...event } 保留对事件的引用。

在 React 事件调用时,React 传递给事件处理程序是一个合成事件对象的实例 SyntheticEvent 是通过合并得到的。 这意味着在事件回调被调用后,SyntheticEvent 对象将被重用并且所有属性都将被取消。 这是出于性能原因,因此,您无法以异步方式访问该事件。React合成事件官方文档
event.persist()
// or
const event: SyntheticEvent = { ...event }

还有一个隐晦点的需要指出,我们知道如果想要使 input 为受控元素,正确的做法是:在给 input 绑定 value 时,需要同时绑定 onChange 事件来监听数据变化,否则就会报如下警告。


但是当你异步传递 SyntheticEvent 对象时,使用 value 属性进行绑定的 input,值不会再发生变化(但它仍是一个受控元素)。

...
event.persist()
timer = setTimeout(() => {
  fn.call(this, event) // 传递 event
}, delay) 
...
<input
  defaultValue={this.state.value}
  // value={this.state.value} 使用 value 属性,值不会发生变化
  onChange={e => this.onChangeHandler(e)}
/>

如下图:



减少 HTTP 请求

减少 HTTP 请求的手段之一就是将 HTTP 请求结果进行缓存,如果下次请求的 url 未发生变化,则直接从缓存中获取数据

import axios from 'axios';
const caches = {}; 
const axiosRequester = () => {
  let cancel;
  return async url => {
    if(cancel) {
      cancel.cancel();
    }
    cancel = axios.CancelToken.source();
    try {
      if(caches[url]) { //如果请求的 url 之前已经提交过,就不在进行请求,返回之前请求回来的数据
        return caches[url];
      }
      const res = await axios.post(url, {
         cancelToken: cancel.token
      })
      const result = res.data.result;
      caches[url] = result;  //将 url作为 key, result 为请求回来的数据,存储起来
      return result;
    } catch(error) {
      if(axios.isCancel(error)) {
        console.log('Request canceled', error.message);
      } else {
        console.log(error.message);
      }
    }
  }
}

export const _search = axiosRequester();

在使用 axios 进行 HTTP 请求时,首先根据 url 判断数据是否已被缓存,如果命中则直接从缓存中拿数据。如果未被缓存,则发起 HTTP 请求,并将请求回来的结果以键值对的形式保存在 caches 对象中。


缓存淘汰策略 - LRU

由于缓存空间是有限的,所以不能无限制的进行数据存储,当存储容量达到一个阀值时,就会造成内存溢出,因此在进行数据缓存时,就要根据情况对缓存进行优化,清除一些可能不会再用到的数据。

这里我们用到 keepAlive 相同的缓存淘汰机制 - LRU。

LRU - 最近最少使用策略

  • 以时间作为参考,如果数据最近被访问过,那么将来被访问的几率会更高,如果以一个数组去记录数据,当有一数据被访问时,该数据会被移动到数组的末尾,表明最近被使用过,当缓存溢出时,会删除数组的头部数据,即将最不频繁使用的数据移除。

实现 LRU 策略我们需要一个存储缓存对象 key 的数组:

const keys = [];

并且需要设置一个阀值,控制缓存栈最大的存储数量:

const MAXIMUN_CACHES = 20;

还需要一个用来删除数组 keys 成员项的工具函数 remove:

function remove(arr, item) {
  if (arr.length) {
    var index = arr.indexOf(item)
    if (index > -1) {
      return arr.splice(index, 1)
    }
  }
}

最后再实现一个 pruneCacheEntry 函数,用来删除最少访问的数据(第一项):

// 传入 keys 数组的第一项
if (keys.length > parseInt(MAXIMUN_CACHES)) {
  pruneCacheEntry(caches, keys[0], keys);
}

...
// 删除最少访问的数据
function pruneCacheEntry ( caches, key, keys) {
  caches[key] = null; // 清空对应的数据
  delete caches[key]; // 删除缓存 key
  remove(keys, key);
}
来自:https://segmentfault.com/a/1190000022876543


站长推荐

1.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

2.广告联盟: 整理了目前主流的广告联盟平台,如果你有流量,可以作为参考选择适合你的平台点击进入

链接: http://www.fly63.com/article/detial/9293

关闭

JS 代码脏乱差?你需要知道这些优化技巧

JavaScript 是万众瞩目的力量。它是世界上最流行的编程语言。它容易理解,有丰富的学习资源,对初学者非常友好。JavaScript 有着庞大的资源库,对小公司和大企业都颇具吸引力。庞大的 JS 工具和库生态系统为开发者的生产力带来了福音

从4个方面优化你的Vue项目

运行时优化:1、使用v-if代替v-show,2、v-for必须加上key,并避免同时使用v-if,3、事件及时销毁。首屏优化:1、图片裁剪、使用webp,2、资源提前请求,3、异步路由,4、异步组件,5、使用轻量级插件、异步插件

移动 H5 首屏加速、优化方案

随着移动设备性能不断增强,web 页面的性能体验逐渐变得可以接受,又因为 web 开发模式的诸多好处(跨平台,动态更新,减体积,无限扩展)

首页白屏优化实践

自从前端三大框架React、Vue、Angular面世以来,前端开发逐渐趋向规范化、统一化,大多数时候新建前端项目,首先想到使用的技术一定是三大框架之一,框架给前端开发带来了极大的便利和规范,但是由于这三大框架都是JS驱动

webpack常用构建优化总览

读了《深入浅出webpack》总结一下常用的webpack的构建优化策略,可通过以下手段来提升项目构建时的速度,理论上我们项目的第三方依赖均应在自己的工程的node_modules下,所以我们可以设置查找目录

webpack优化分享

Webpack 就像一条生产线,要经过一系列处理流程后才能将源文件转换成输出结果。 这条生产线上的每个处理流程的职责都是单一的,多个流程之间有存在依赖关系,只有完成当前处理后才能交给下一个流程去处理

React 中 的 9 种优化技术

谷歌的数据表明,一个有 10 条数据 0.4 秒可以加载完的页面,在变成 30 条数据加载时间为 0.9 秒后,流量和广告收入减少了 20%。当谷歌地图的首页文件大小从 100kb 减少到 70~80kb 时,流量在第一周涨了 10%,接下来的三周涨了 25%

网站搜索引擎优化,值得关注的4个策略有哪些?

在做网站搜索引擎优化的过程中,对于企业站而言,由于SEO人员都是处于执行层面,甚至即使你有权制定SEO优化方案,偶尔也是草草就上手操作。当你运营到一定阶段的时候

通过HTML5 Video来优化动态GIF

网页中的动态GIF图片是非常受欢迎的,因为它们相比静态图片更生动,相比网页视频更简单。但是GIF图片通常具有较大的体积,就导致网页加载速度变慢,内存使用增加

编写高质量可维护的代码——异步优化

在现在前端开发中,异步操作的频次已经越来越高了,特别对于数据接口请求和定时器的使用,使得我们不得不关注异步在业务中碰到的场景,以及对异步的优化。错误的异步处理可能会带来很多问题,诸如页面渲染、重复加载等问题

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!