探索 Vue SWRV 的快取函式庫
Opened this issue · 0 comments
SWR: stale-while-revalidate
今天會用到的新東東
- SWRV: 支援 Vue3 的 SWR 套件
- composition API: Vue3 的新語法
- json server: 取代平常 express mock server 的接近 restful api 方式假後端
SWR: stale-while-revalidate (好像中文叫非同步更新快取)
SWR 會在你要從 API 取得資料的時候先確認之前有沒有取過了,如果有取過就先送前一次的給你,等到 API 把資料送到前端的時候,再確認資料一不一樣,如果不一樣的話就再把新的資料存起來,然後送給你更新畫面!
基本款程式碼
📃 /src/composition/HelloWorld.vue
import useSWRV from 'swrv'
const fetcher = (url) => {
return fetch(url).then(r => r.json())
}
// 使用 const let 宣告 function 時 hoisting 的行為 不同於 var ,記得宣告於使用之前。
// 不然就直接使用 function fetcher () {} 宣告就好囉!
setup() {
const { data: articles, error: articlesError } = useSWRV('/articles', fetcher)
// data 取得成功時會是 data,失敗時是 undefined
// error 取得成功時是 undefined,失敗時是 message
// 這兩個變數都是 ref (ref 在 composition api 中才能響應資料到畫面上)
// fetcher 是一個 return promise 的 function
return {
articles,
articlesError,
}
}
為什麼要建立 fetcher 這個函數
- fethcer 可以做為一個抽象函數重複使用,而且不用去關注底下用的 library 是原生地 fetch 還是 axios 之類的 promise function
- fetcher 中的 url 參數會被做為 SWRV 的 cache key
測試 useSWRV 的效用
- 建立另一個 components
- 與第一個 components 引入同一個 fetch api
📃 /src/components/Count.vue
import { fetcher } from '../utils/fetcher.js'
import useSWRV from 'swrv'
export default {
name: 'Count',
setup() {
const { data: articles } = useSWRV('/articles', fetcher)
return {
articles
}
},
}
📃 /src/utils/fetcher.js
export const fetcher = (url) => {
console.log('fetching ...'); // ADD
return fetch(url)
.then(r => r.json())
}
Revalidation 特性
Revalidation 表示的是特定情況下,不會使用 cache 處理 request
Q: 什麼情況下會使用 cache ?
A: 一連串的相同 promise function 時, useSWRV 能直接回傳 cache 回來
(promise return 表示還沒得到資料)
📃 /src/composition/HelloWorld.vue
新增 setTimeOut 讓 Count 元件延遲出現
import { fetcher } from '../utils/fetcher.js'
import { ref } from 'vue'
import useSWRV from "swrv";
import Count from './Count.vue'
export default {
name: 'App',
components: { Count },
setup() {
const { data: articles, error: articlesError } = useSWRV('/articles', fetcher)
const showCount = ref(false) // 用 ref 建立的變數,才能動態響應到 template 中
setTimeout(() => showCount.value = true, 3000) // ref 的值必須 .value 才能修改
return {
articles,
articlesError,
showCount,
}
},
}
這種情況下元件不會使用 cache 的資料,
原因是 Count 元件中的 fetch function 跟 HelloWorld 中的 fetch function
不是一個累積的未處理完成的 promise return (Event loop 那件事)
所以會被視作是不同次的呼叫。
(附帶一提在切換分頁回來時, useSWRV 也會幫我們重新呼叫 API,來取得最新資料)
Mutation
Q: 候我們可能需要強制更新資料時,該怎麼做呢?
A: 透過呼叫 mutate 這個 useSWRV的 回傳 function,可以送出強制的 request,來避免持續 cache
export default {
name: 'App',
components: { Count },
setup() {
const {
data: articles,
error: articlesError,
mutate: mutateArticles
} = useSWRV('/articles', fetcher)
const BtnClickedCount = ref(0)
const updateArticles = () => {
mutateArticles()
BtnClickedCount.value += 1
}
const showCount = ref(false)
setTimeout(() => showCount.value = true, 3000)
return {
articles,
articlesError,
showCount,
updateArticles,
BtnClickedCount,
}
},
}
POST Request
更新資料後避免快取的做法如下
const updateArticles = async () => {
await fetch('http://localhost:3000/articles', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
"title": 'Untitled - ' + new Date().toString(),
"author": "Andy",
"link": "https://vuemastery.com/blog",
})
})
mutateArticles()
BtnClickedCount.value += 1
}
Centralized Store
若需要一個全域的 store 機制(VueX),也可以透過 SWRV 做到
const {
data: hideList, // 這個 hideList 是一個 ref 預設值為空陣列
mutate: mutateHideList
} = useSWRV('hideList', () => []) // 也可以不用回傳 promise function
const visibleArticles = computed(() => {
if(articles.value === undefined) {
return articles.value
}
return articles.value.filter(a => hideList.value.indexOf(a.id) === -1)
})
const hideArticle = (id) => {
mutateHideList(() => hideList.value = [...hideList.value, id])
}
composition API
setup
能夠把所有的 vue2 內容寫在 setup 中
要注意的點是只有 return 的值才能被 template 獨到
另外 setup 中是沒有辦法用 this 取得 vue2 的那些各種變數的
<template>
{{ count }} // 點完 add button 這個不會變
{{ refCount }}
<button onClick="addCount"></button>
</template>
import { ref } from 'vue'
export default {
name: 'Count',
setup(props, context) {
// props 就跟以前一樣,因為現在沒有 this 了
// context 裡面可取得 context.attrs context.slots context.emit
const count = 0
const refCount = ref(0)
const addCount = () = {
count += 1
refCount.value += 1
}
return {
count,
refCount,
addCount
}
},
}
lifecycle hook
import { onMounted, onUpdated, onUnmounted } from 'vue'
const MyComponent = {
setup() {
onMounted(() => {
console.log('mounted!')
})
onUpdated(() => {
console.log('updated!')
})
onUnmounted(() => {
console.log('unmounted!')
})
}
}
reactive
reactive 與 ref 的不同之處在於
reactive 中只能放物件
好處是 reactive 不需要用 .value 來改變值了
setup() {
const obj = reactive({ count: 0 })
obj.count += 1
return obj
}
toRefs
用來把
- 外部傳入的變數
- 解構賦的 reactive 變數
再度轉為 ref 時所使用的 function
json server
install
npm install -g json-server
db.json
{
"articles": [
{
"title": "GraphQL Client with Vue.js",
"author": "Andy",
"link": "https://www.vuemastery.com/blog/part-3-client-side-graphql-with-vuejs",
"id": 1
},
{
"title": "TypeScript with Vue.js",
"author": "Andy",
"link": "https://www.vuemastery.com/blog/getting-started-with-typescript-and-vuejs",
"id": 2
}
]
}
command
json-server --watch db.json
串接方式
現在針對網址 http://localhost:3000/articles
get 可以得到內容
post 可以新增內容(是真的會寫入新資料到 db.json 中)
參考
https://www.vuemastery.com/blog/data-fetching-and-caching-with-swr-and-vuejs/
https://book.vue.tw/