首页 > 编程语言 >小程序优化之旅(三) -- 请求缓存与预请求优化

小程序优化之旅(三) -- 请求缓存与预请求优化

时间:2023-05-15 09:36:53浏览次数:48  
标签:缓存 const 请求 -- 跳转 performance 优化 页面

一、预请求概念

首先在一开始还是先明确下这里所提及到的“预请求”的概念和常规的 http 的 options 请求有所区别,这篇文章所涉及到的预请求的概念都是在页面切换时候的页面请求的提请发送,跳转进入新页面后能够快速的获取到服务端的数据。

1.1 预请求的业务含义

为啥需要要做这个预请求的处理呢?很明显都是为了能够提升应用的运行性能,进而提升用户的使用体验。


1.2 预请求的构思

实现目标

在进入页面跳转前预先发送请求,跳转页面后获取对应的请求结果,实现请求和切换页面一起异步进行。

技术根基与原理:

Request 请求结果的缓存与获取。

实现思路:

比较简单的实现就是对 fetch 请求方法进行一个请求缓存的封装操作,然后在页面跳转前进行一个设置缓存的请求,跳转页面后再进行从请求缓存集当中获取请求结果。





二、具体封装与改造

2.1 封装请求处理

封装请求 fetch hooks

  • 请求结果缓存的实现
    • 简单暴力的实现就是将请求结果缓存到 Map 结构对象当中
  • 判断读取缓存的请求结果
    • 再次请求时候判断是否需要缓存并且在缓存集 Map 当中是否已经有缓存结果
  typescript 复制代码
// /plugins/http.ts

export const $request = ({ url = '', method = 'GET', data }: { url: string, method?: 'OPTIONS' | 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'TRACE' | 'CONNECT', data?: any }): Promise<any> => {
  return new Promise((resolve, reject) => {
    uni.request({
      url, data, method,
      success: res => resolve(res),
      fail: err => reject(err),
    })
  })
}
  typescript 复制代码
// hooks/useFetch.ts

import { ref, onBeforeUnmount, } from 'vue'

import { $request as httpRequest } from '@/plugins/http'

import usePreFetch from '@/hooks/usePreFetch'

const FETCH_RESPONSE_CACHE_MAP = new Map()      // 请求成功结果缓存集

/**
 * 生成 Key
 */
const _generateKey = (data: any) => {
  return JSON.stringify(JSON.parse(JSON.stringify(data || '')))
}

export default function (data: any) {
  let fetchRes = null
  let useCache = !!(data.useCache || false) // 需要请求缓存 OR 需要预请求
  const _fetchKey = _generateKey(data)
  const fetchLoading = ref(false)

  const httpFetch = async () => {
    fetchLoading.value = true

    // 判断是否需要使用缓存并且是否已经有缓存了则从请求缓存集当中获取对应缓存结果并返回
    if (useCache && FETCH_RESPONSE_CACHE_MAP.get(_fetchKey)) {
      fetchRes = FETCH_RESPONSE_CACHE_MAP.get(_fetchKey)

      FETCH_RESPONSE_CACHE_MAP.delete(_fetchKey)
      
      return fetchRes
    }

    // 执行请求逻辑,获取请求结果
		fetchRes = await httpRequest(data)

    // 需要缓存请求时将请求结果 Promise 封装并且设置在请求缓存集中
    if (useCache) {
      FETCH_RESPONSE_CACHE_MAP.set(_fetchKey, Promise.resolve(fetchRes))
    }

    fetchLoading.value = false

    return fetchRes
  }
  
  return {
    fetchLoading,
    http: httpFetch,
  }
}

2.2 改造使用

跳转前预请求

  • 在调用 navigationTo 进行跳转前设置一个 useCache 参数告诉 useFetch 需要进行请求的缓存处理

跳转后获取请求结果

  • 跳转后再次请求,封装的 useFetch hooks 方法内部去获取缓存请求集当中获取对应请求的缓存
  dart 复制代码
// 封装的请求方法
const getXyzInfo = (id = '', useCache = false) => {
  const {
    http,
  } = useFetch({
    useCache,
    url: 'xyz/info',
    data: {
      id,
    },
  })

  return http()
}


// 跳转时候逻辑
getXyzInfo('xxxx', true)
uni.navigationTo({
  url: 'pages/xyz/index',
})


// 跳转后页面的生命周期
onMounted(async () => {
  const res = await getXyzInfo('xxxx', true)
  // ...
})





三、优化结果

3.1 性能优化的指标

传统的白屏时间、首屏时间

使用Performance.timingapi 提供的时间节点进行计算页面的一些时间指标。

白屏时间:浏览器开始显示内容的时间,所以我们一般认为解析完的时刻,或者开始渲染标签就是该页面白屏结束的时间。

  • performance.timing.domLoading - performance.timing.navigationStart

首屏时间:首屏时间是指用户打开一个网站时,直到浏览器首页面内容渲染完成的时间。

  • performance.timing.domInteractive - performance.timing.navigationStart

虽然白屏时间和首屏时间能够一定程度展现这个预请求技术改造的性能优化指标,但是感觉还未能算精确的。

更为精准的统计时间

更精准的时间应该是从上一个页面进行调用跳转的 api 到进入新页面开始接受到请求响应回来的时间,因此这里是使用Performance的实例 api 进行一个时间指标的收集操作:

  • 在封装的路由跳转方法navigateTo当中利用performance.mark手动打点这次跳转开始的时间;
  • 在业务页面当中请求fetch成功返回的回调当中再次通过performance.mark打点当前时间节点的时间戳;
  • 通过performance.measure计算得出适合当前场景的较为精准的统计时间。
  javascript 复制代码
// 跳转时候逻辑
performance.mark('preFetchAndNavigationTiming-start')
getXyzInfo('xxxx', true)
uni.navigationTo({
  url: 'pages/xyz/index',
})


// 跳转后页面的生命周期
onMounted(async () => {
  const res = await getXyzInfo('xxxx', true)
  performance.mark('preFetchAndNavigationTiming-end')
  
  // ...

  computedPreFetchAndNavigationTiming()
})

// 封装的获取统计时间方法
function computedPreFetchAndNavigationTiming() {
  performance.measure('preFetchAndNavigationTiming', 'preFetchAndNavigationTiming-start', 'preFetchAndNavigationTiming-end')
  const measures = performance.getEntriesByName('preFetchAndNavigationTiming')
  const measure = measures[0]

	console.info('preFetchAndNavigationTiming milliseconds: ', measure.duration)
  
  // 清除存储的标志位
  performance.clearMarks();
  performance.clearMeasures();
}

3.2 优化的成效

通过上一小节的统计指标的测量,在一个正常网络的正常手机上在页面跳转接入预请求改造后可提升节约了大概 200 - 300ms 的时间,算是证明这个预请求的改造是有一定程度的性能提升,虽然带给用户的体验的提升可能并不算很明显。


3.3 思考其中的不足

更有效的缓存优化策略

目前项目当中只是仅仅粗暴的做了一个 Map 的数据结构对象简单暴力的对请求缓存进行一个存储,但是更有效的缓存策略可以参考 LRU 算法对请求结果缓存的进行一个优化储存策略改造。这里就不再展开,参考资料内有相关的文章,可根据实际自寻。

更科学与智能化的预请求配置

目前还是需要在业务跳转前进行一个预请求的 fetch 调用,跳转后再次调用 fetch 获取缓存;在跳转路由时候存在一个配置化,在封装的 fetch hooks 里面自动调用 preFetch 预请求,避免业务方在多次跳转重复多次写预请求的逻辑代码。

 

标签:缓存,const,请求,--,跳转,performance,优化,页面
From: https://www.cnblogs.com/shusonghe/p/17400849.html

相关文章

  • 第十三篇——通达信指标公式绘图函数简介——自定义指标颜色、线型等(从零起步编写通达
    内容提要:本文简单介绍了通达信指标公式绘图函数的三种类型——画线函数、标记函数、修饰函数。 在之前的8篇文章中,介绍了通达信指标公式编写常用的18个函数。开始讲的几个函数比较简单,到后面一些函数理解起来都比较困难,更别说使用了,大家在看文章的时候可能也是云里雾里。学......
  • 2023.5.11
       Java多线程是指在一个Java程序中同时执行多个线程,它可以提高程序的并发性和响应能力。Java中实现多线程的方式:       继承Thread类       实现Runnable接口       Executor框架       Callable       Future       线程池......
  • [每天例题]蓝桥杯 C语言 字符统计
    字符统计题目思路分析1.建立字符数组,存储字符串2.建立整形数组,储存对应字母出现的次数3.使用for循环进行排序,使用if判断最大最小值代码#include<stdio.h>intmain(){chara[1000000];intnum[26]={0};inti;intmax=0;scanf("%s",&a);......
  • 实习记录模板
    计划删减代码,把它变成自己的,准备答辩学习前端知识angular框架,html语法扎实的学,css,JavaScript学习后端框架,Java语言学扎实点知道接口怎么回事,尝试或明白一个接口怎么写,接口调试是怎么实现的解决配置文件中resources中的几千个报错,不解决,无意义要搞明白数据库中的字段......
  • ET框架6.0分析二、异步编程
    概述ET框架很多地方都用到了异步,例如资源加载、AI、Actor模型等等。ET框架对C#的异步操作进行了一定程度的封装和改造,有一些特点:显式的或者说强调了使用C#异步实现协程机制(其实C#的异步编程天生就能实现这种用法)强制单线程异步没有使用C#库的Task,自己实现了ETTask等类实现了......
  • 2023.5.10
    什么是多线程   多线程:       指的是在一个进程中同时运行多个线程,每个线程都可以独立执行不同的任务或操作。       与单线程相比,多线程可以提高程序的并发性和响应能力。什么是进程   进程:   是指正在运行的程序的实例。   每个进程都拥有自己......
  • 2023-05-15 leetcode周赛题
    找出转圈游戏输家mysolution100%passclassSolution:defcircularGameLosers(self,n:int,k:int)->List[int]:seen=set()now_num=1step=1seen.add(1)while1:stepSum=step*ktotal=now_num+stepSumnow_num=tot......
  • 小程序优化之旅(四) -- 项目持续化集成与自动化上传代码
    一、前情提要成1.1改造的目的概述在开发小程序的完成项目流程当中,免不了需要上传代码到微信平台,这个处理在以前是只能通过小程序开发者工具界面进行人工手动点击按钮进行;这个过程是十分枯燥并且一定程度上消耗了宝贵的人力资源。后续微信提供了小程序的CI工具,正式进入通过跑......
  • 2023.5.12
    实现Runnable接口 另一种实现多线程的方式是实现Runnable接口,需要实现run()方法,并将实现了Runnable接口的对象传递给Thread类的构造函数。publicclassRunnableDemo{   publicstaticvoidmain(String[]args){       //创建10个线程并启动       fo......
  • AI DevOps | ChatGPT 与研发效能、效率提升(中)
    为啥ChatGPT突然火了?简单概括就是:产品太过惊艳,体验超预期之前人工智能发展多年,报道最多的也许就是曾经的李世石大战AlphaGo,现实中的特斯拉自动驾驶,还有波士顿动能放出的机器狗。对于圈外人士来说一般也接触不到这些,仅仅看看而已。但是ChatGPT不一样,一声巨响,石头中蹦出一个C......