最终在IDB上的成品效果
前言
每次都说把Api介绍一遍有什么?这次我送你们一个完整的可运行的代码案例。各位自取
在第一次通过网络获取之后,在时间范围内从本地读取,不再请求网络。各位可以试试
现在来实现一个类似的SWR的Vue自定义hooks,使用axios作为数据获取工具、idb作为本地缓存工具
安装依赖:
npm install axios idb-keyval
接下来,我们可以创建一个名为 useSWR.js
的hooks,该hooks将接收三个参数:
key
:用于区分不同数据的关键字。fetcher
:用于获取数据的函数。options
:用于配置SWR行为的选项对象。
自定义hooks useSWR.js
import { reactive, toRefs, watchEffect } from 'vue' // 引入 Vue3 响应式数据工具包
import { get as idbGet, set as idbSet } from 'idb-keyval' // 引入 IndexedDB 操作库
const defaultOptions = {
refreshInterval: 0, // 缓存刷新的时间间隔,默认不自动刷新
ttl: Infinity // 缓存的有效时间,默认为永久有效
}
// 定义一个 useSWR 函数,接收三个参数
export default function useSWR(key, fetcher, options = {}) {
// 使用 reactive 函数创建响应式数据对象
const state = reactive({
data: null, // 存储返回的数据
error: null, // 存储请求发生的错误
isLoading: false // 是否正在请求数据
})
// 合并默认选项和传入的选项
const { refreshInterval, ttl } = Object.assign({}, defaultOptions, options)
// 定义一个 fetch 函数,用于获取数据
async function fetch() {
state.isLoading = true // 数据正在加载中,设置 isLoading 属性为 true
try {
// 调用 fetcher 函数获取数据
const data = await fetcher()
state.data = data // 将获取到的数据存储在 data 属性中
state.error = null // 请求成功,将 error 属性设置为 null
state.isLoading = false // 数据请求成功,设置 isLoading 属性为 false
idbSet(key, { data, timestamp: Date.now() }) // 将获取到的数据存储在 IndexedDB 数据库中
} catch (error) {
state.error = error // 请求发生错误,将 error 属性设置为错误信息
state.isLoading = false // 数据请求失败,设置 isLoading 属性为 false
}
}
// 定义一个 fetchIfNeeded 函数,用于检查是否需要重新获取数据
async function fetchIfNeeded() {
// 从 IndexedDB 中获取缓存数据
const cachedData = await idbGet(key)
// 如果存在缓存数据,并且缓存未过期,则直接使用缓存数据
if (cachedData && (Date.now() - cachedData.timestamp < ttl)) {
state.data = cachedData.data
return
}
// 如果不存在缓存数据或者缓存已过期,则重新获取数据
await fetch()
}
// 使用 watchEffect 监听 state 数据对象的变化
watchEffect(() => {
fetchIfNeeded() // 执行 fetchIfNeeded 函数,检查是否需要重新获取数据
// 如果设置了缓存刷新时间,则使用 setInterval 定时器自动刷新缓存
if (refreshInterval > 0) {
const intervalId = setInterval(fetchIfNeeded, refreshInterval)
// 返回一个函数,用于在组件销毁前清除定时器
return () => clearInterval(intervalId)
}
})
// 使用 toRefs 函数响应
return toRefs(state)
}
代码解释
导入了Vue3的一些API,以及idb-keyval。
然后,我们定义了一些默认选项,用于在options参数未提供时使用。
接下来,我们定义了一个名为 useSWR
的函数,该函数接收三个参数。在函数内部,我们首先创建了一个响应式的 state
对象,其中包含了 data
、error
和 isLoading
三个属性。
然后,我们解构出 refreshInterval
和 ttl
选项,并为它们提供了默认值。接下来,我们定义了一个名为 fetch
的函数,该函数用于获取数据,并将结果保存到IDB中。
在 fetchIfNeeded
函数中,我们首先从IDB中获取缓存的数据。如果缓存的数据存在且没有过期,则直接将其设置为state.data。否则,我们将调用 fetch
函数来获取最新的数据。
在 watchEffect
中,我们首先调用 fetchIfNeeded
函数来尝试从缓存中获取数据。然后,如果 refreshInterval
大于0,我们使用 setInterval函数来定期调用
fetchIfNeeded函数以更新数据。最后,我们返回一个通过
toRefs函数转换后的
state` 对象,以便在Vue3中可以直接访问其属性。
现在,我们可以使用 useSWR
hooks 来获取和显示数据。下面是一个例子:
使用案例
<template>
<div>
<div v-if="isLoading">Loading...</div>
<div v-if="error">Error: {{ error.message }}</div>
<ul v-if="data">
<li v-for="item in data" :key="item.id">{{ item.title }}</li>
</ul>
</div>
</template>
<script>
import { defineComponent } from 'vue'
import useSWR from './useSWR'
import axios from 'axios'
export default defineComponent({
setup() {
const { data, error, isLoading } = useSWR(
'todos',
async () => {
const response = await axios.get('https://jsonplaceholder.typicode.com/todos')
return response.data
},
{ refreshInterval: 5000 }
)
return { data, error, isLoading }
}
})
</script>
总结
在这个例子中,我们使用 useSWR
hooks 来获取名为 ‘todos’ 的数据,并将其显示为一个无序列表。我们传递了一个 fetcher
函数来获取数据,该函数将从远程API获取数据。我们还传递了一个 options
对象来配置SWR行为,例如定期更新数据的间隔时间。
最后,我们使用Vue3的 defineComponent
函数来定义一个Vue组件,然后将 useSWR
hooks 返回的数据绑定到模板中的相应元素上。
这就是使用axios、idb-keyval和Vue3实现完整的SWR的hooks的步骤。SWR使我们能够在减少网络请求次数的同时,保持UI的及时更新。通过使用本地缓存,我们可以减少网络请求,而使用Vue3的响应式系统则使我们能够实时更新UI。
原文链接:https://juejin.cn/post/7227715728362651709 作者:布衣1983