跳到主要内容

缓存 & SWR

如果设置了 options.cacheKeyuseRequest 会将当前请求成功的数据缓存起来。下次组件初始化时,如果有缓存数据,我们会优先返回缓存数据,然后在背后发送新请求,也就是 SWR 的能力。

你可以通过 options.staleTime 设置数据保持新鲜时间,在该时间内,我们认为数据是新鲜的,不会重新发起请求。

你也可以通过 options.cacheTime 设置数据缓存时间,超过该时间,我们会清空该条缓存数据。

接下来通过几个例子来体验缓存这些功能。

SWR

下面的示例,我们设置了 cacheKey,在组件第二次加载时,会优先返回缓存的内容,然后在背后重新发起请求。你可以通过点击按钮来体验效果。

network/useRequest/cache/cacheKey
import React from 'react';
import DemoContent from '@src/components/DemoContent';
import { Button, Field } from '@taroify/core';

import { useRequest } from 'taro-hooks';
import { useBoolean } from '@taro-hooks/ahooks';
import Mock from 'mockjs';

async function getArticle(): Promise<{ data: string; time: number }> {
console.log('cacheKey');
return new Promise((resolve) => {
setTimeout(() => {
resolve({
data: Mock.mock('@paragraph(4)'),
time: new Date().getTime(),
});
}, 1000);
});
}

const Article = () => {
const { data, loading } = useRequest(getArticle, {
cacheKey: 'cacheKey-demo',
});
if (!data && loading) {
return <Field>Loading</Field>;
}
return (
<>
<Field>Background loading: {loading ? 'true' : 'false'}</Field>
<Field>Latest request time: {data?.time}</Field>
<Field>{data?.data}</Field>
</>
);
};

export default () => {
const [state, { toggle }] = useBoolean();

return (
<DemoContent title="缓存 & SWR">
<Field align="center">
<Button color="primary" size="small" block onClick={() => toggle()}>
show / hidden
</Button>
</Field>
{state && <Article />}
</DemoContent>
);
};

数据保持新鲜

通过设置 staleTime,我们可以指定数据新鲜时间,在这个时间内,不会重新发起请求。下面的示例设置了 5s 的新鲜时间,你可以通过点击按钮来体验效果

network/useRequest/cache/staleTime
import React from 'react';
import DemoContent from '@src/components/DemoContent';
import { Button, Field } from '@taroify/core';

import { useRequest } from 'taro-hooks';
import { useBoolean } from '@taro-hooks/ahooks';
import Mock from 'mockjs';

async function getArticle(): Promise<{ data: string; time: number }> {
console.log('cacheKey-staleTime');
return new Promise((resolve) => {
setTimeout(() => {
resolve({
data: Mock.mock('@paragraph()'),
time: new Date().getTime(),
});
}, 1000);
});
}

const Article = () => {
const { data, loading } = useRequest(getArticle, {
cacheKey: 'staleTime-demo',
staleTime: 5000,
});
if (!data && loading) {
return <Field>Loading</Field>;
}
return (
<>
<Field>Background loading: {loading ? 'true' : 'false'}</Field>
<Field>Latest request time: {data?.time}</Field>
<Field>{data?.data}</Field>
</>
);
};

export default () => {
const [state, { toggle }] = useBoolean();

return (
<DemoContent title="缓存 & SWR - 数据保持新鲜">
<Field align="center">
<Button color="primary" size="small" block onClick={() => toggle()}>
show / hidden
</Button>
</Field>
{state && <Article />}
</DemoContent>
);
};

数据共享

同一个 cacheKey 的内容,在全局是共享的,这会带来以下几个特性

  • 请求 Promise 共享,相同的 cacheKey 同时只会有一个在发起请求,后发起的会共用同一个请求 Promise
  • 数据同步,任何时候,当我们改变其中某个 cacheKey 的内容时,其它相同 cacheKey 的内容均会同步

下面的示例中,初始化时,两个组件只会发起一个请求。并且两篇文章的内容永远是同步的。

network/useRequest/cache/share
import React from 'react';
import DemoContent from '@src/components/DemoContent';
import { Button, Field } from '@taroify/core';

import { useRequest } from 'taro-hooks';
import Mock from 'mockjs';

async function getArticle(): Promise<{ data: string; time: number }> {
console.log('cacheKey-share');
return new Promise((resolve) => {
setTimeout(() => {
resolve({
data: Mock.mock('@paragraph()'),
time: new Date().getTime(),
});
}, 3000);
});
}

const Article = () => {
const { data, loading, refresh } = useRequest(getArticle, {
cacheKey: 'cacheKey-share',
});
if (!data && loading) {
return <Field>Loading</Field>;
}
return (
<>
<Field>Background loading: {loading ? 'true' : 'false'}</Field>
<Field align="center">
<Button
loading={loading}
color="primary"
size="small"
block
onClick={() => refresh()}
>
更新
</Button>
</Field>

<Field>Latest request time: {data?.time}</Field>
<Field>{data?.data}</Field>
</>
);
};

export default () => {
return (
<DemoContent title="缓存 & SWR - 数据共享">
<Field>Article 1</Field>
<Article />
<Field>Article 2</Field>
<Article />
</DemoContent>
);
};

参数缓存

缓存的数据包括 dataparams,通过 params 缓存机制,我们可以记忆上一次请求的条件,并在下次初始化。

下面的示例中,我们可以从缓存的 params 中初始化 keyword

network/useRequest/cache/params
import React from 'react';
import DemoContent from '@src/components/DemoContent';
import { Button, Field, Input } from '@taroify/core';

import { useRequest } from 'taro-hooks';
import { useState } from '@taro-hooks/core';
import { useBoolean } from '@taro-hooks/ahooks';
import Mock from 'mockjs';

async function getArticle(
keyword: string,
): Promise<{ data: string; time: number }> {
console.log('cacheKey', keyword);
return new Promise((resolve) => {
setTimeout(() => {
resolve({
data: Mock.mock('@paragraph()'),
time: new Date().getTime(),
});
}, 1000);
});
}

const Article = () => {
const { data, params, loading, run } = useRequest(getArticle, {
cacheKey: 'cacheKey-demo',
});

const [keyword, setKeyword] = useState(params[0] || '');

if (!data && loading) {
return <Field>Loading</Field>;
}
return (
<>
<Field align="center">
<Input value={keyword} onChange={(e) => setKeyword(e.detail.value)} />
<Button color="primary" size="small" onClick={() => run(keyword)}>
Get
</Button>
</Field>
<Field>Background loading: {loading ? 'true' : 'false'}</Field>
<Field>Latest request time: {data?.time}</Field>
<Field>Keyword: {keyword}</Field>
<Field>{data?.data}</Field>
</>
);
};

export default () => {
const [state, { toggle }] = useBoolean();

return (
<DemoContent title="缓存 & SWR - 参数缓存">
<Field align="center">
<Button color="primary" size="small" block onClick={() => toggle()}>
show / hidden
</Button>
</Field>
{state && <Article />}
</DemoContent>
);
};

删除缓存

taro-hooks 提供了一个 clearCache 方法,可以清除指定 cacheKey 的缓存数据。

警告

vue中清除缓存的例子不太好实现. 组件卸载原则的问题.

network/useRequest/cache/clearCache
import React from 'react';
import DemoContent from '@src/components/DemoContent';
import { Button, Field } from '@taroify/core';

import { useRequest, clearCache } from 'taro-hooks';
import { showToast } from '@tarojs/taro';
import { useBoolean } from '@taro-hooks/ahooks';
import Mock from 'mockjs';

async function getArticle(): Promise<{ data: string; time: number }> {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
data: Mock.mock('@paragraph(4)'),
time: new Date().getTime(),
});
}, 3000);
});
}

const Article = ({ cacheKey }) => {
const { data, loading } = useRequest(getArticle, {
cacheKey,
});

console.log(data, Mock.mock('@paragraph()'));

if (!data && loading) {
return <Field>Loading</Field>;
}
return (
<>
<Field>Background loading: {loading ? 'true' : 'false'}</Field>
<Field>Latest request time: {data?.time}</Field>
<Field>{data?.data}</Field>
</>
);
};

const clear = (cacheKey?: string | string[]) => {
clearCache(cacheKey);
const tips = Array.isArray(cacheKey) ? cacheKey.join('、') : cacheKey;
showToast({
title: 'Clear ${tips ?? 'All'} finished',
icon: 'success',
mask: true,
});
};

export default () => {
const [state, { toggle }] = useBoolean();

return (
<DemoContent title="缓存 & SWR - 删除缓存">
<Field align="center">
<Button color="primary" size="small" block onClick={() => toggle()}>
show / hidden
</Button>
</Field>
<Field align="center">
<Button
color="primary"
size="small"
block
onClick={() => clear('Article1')}
>
clear Article1
</Button>
</Field>
<Field align="center">
<Button
color="primary"
size="small"
block
onClick={() => clear('Article2')}
>
clear Article2
</Button>
</Field>
<Field align="center">
<Button
color="primary"
size="small"
block
onClick={() => clear(['Article2', 'Article3'])}
>
clear Article2 and Article3
</Button>
</Field>
<Field align="center">
<Button color="primary" size="small" block onClick={() => clear()}>
clear all
</Button>
</Field>
<Field>Article 1</Field>
{state && <Article cacheKey="Article1" />}
<Field>Article 2</Field>
{state && <Article cacheKey="Article2" />}
<Field>Article 3</Field>
{state && <Article cacheKey="Article3" />}
</DemoContent>
);
};

自定义缓存

通过配置 setCachegetCache,可以自定义数据缓存,比如可以将数据存储到 localStorageIndexDB 等。

请注意:

  1. setCachegetCache 需要配套使用。
  2. 在自定义缓存模式下,cacheTimeclearCache 不会生效,请根据实际情况自行实现。
network/useRequest/cache/setCache
import React from 'react';
import DemoContent from '@src/components/DemoContent';
import { Button, Field } from '@taroify/core';

import { setStorageSync, getStorageSync } from '@tarojs/taro';
import { useRequest } from 'taro-hooks';
import { useBoolean } from '@taro-hooks/ahooks';
import Mock from 'mockjs';

async function getArticle(): Promise<{ data: string; time: number }> {
console.log('cacheKey');
return new Promise((resolve) => {
setTimeout(() => {
resolve({
data: Mock.mock('@paragraph()'),
time: new Date().getTime(),
});
}, 1000);
});
}

const cacheKey = 'setCache-demo';

const Article = () => {
const { data, loading } = useRequest(getArticle, {
cacheKey,
setCache: (responseData) =>
setStorageSync(cacheKey, JSON.stringify(responseData)),
getCache: () => JSON.parse(getStorageSync(cacheKey) || '{}'),
});
if (!data && loading) {
return <Field>Loading</Field>;
}
return (
<>
<Field>Background loading: {loading ? 'true' : 'false'}</Field>
<Field>Latest request time: {data?.time}</Field>
<Field>{data?.data}</Field>
</>
);
};

export default () => {
const [state, { toggle }] = useBoolean();

return (
<DemoContent title="缓存 & SWR - 自定义缓存">
<Field align="center">
<Button color="primary" size="small" block onClick={() => toggle()}>
show / hidden
</Button>
</Field>
{state && <Article />}
</DemoContent>
);
};

API

interface CachedData<TData, TParams> {
data: TData;
params: TParams;
time: number;
}

Options

参数说明类型默认值
cacheKey请求唯一标识。如果设置了 cacheKey,我们会启用缓存机制。同一个 cacheKey 的数据全局同步。string-
cacheTime
  • 设置缓存数据回收时间。默认缓存数据 5 分钟后回收
  • 如果设置为 -1, 则表示缓存数据永不过期
number300000
staleTime
  • 缓存数据保持新鲜时间。在该时间间隔内,认为数据是新鲜的,不会重新发请求
  • 如果设置为 -1,则表示数据永远新鲜
number0
setCache
  • 自定义设置缓存
  • setCachegetCache 需要配套使用
  • 在自定义缓存模式下,cacheTimeclearCache 不会生效,请根据实际情况自行实现。
(data: CachedData) => void;-
getCache自定义读取缓存(params: TParams) => CachedData-

clearCache

import { clearCache } from 'taro-hooks';

clearCache(cacheKey?: string | string[]);
  1. 支持清空单个缓存,或一组缓存
  2. 如果 cacheKey 为空,则清空所有缓存数据

备注

  • 只有成功的请求数据才会缓存
  • 缓存的数据包括 dataparams