業務場景
在不少業務場景下,我們需要實現簡單的請求緩存(即某個請求只發起一次請求),例如上傳 Token 的獲取、獲取配置的接口等。
這些接口可以通過 Promise 實現簡單的緩存並能夠控制更新,而不需要另外引入緩存層。
示範代碼
用七牛上傳作例子,一般我們會把七牛上傳封裝爲一個單獨的 Upload 組件,外部只需要調用組件,而 token 的獲取封裝到組件內部實現。
//Upload.vue
let fetchToken = null;
export default {
data() {
return {
token: ''
};
},
methods: {
async upload() {
try {
// ...
}
catch(err) {
alert(err.message);
this.refreshToken();
}
},
refreshToken() {
fetchToken = null;
this.fetchToken();
},
fetchToken() {
if (!fetchToken) {
fetchToken = request.get('/api/qiniu/token');
}
try {
this.token = await fetchToken;
}
catch(err) {
console.error(err);
}
}
},
created() {
this.fetchToken();
}
};
上面是一個簡單的緩存上傳 token 的例子,並且會在上傳失敗時刷新 token。
與直接緩存 Token 的值比較,緩存請求有什麼好處?
// 緩存值的代碼
export default {
methods: {
fetchToken() {
if (!fetchToken) {
fetchToken = await request.get('/api/qiniu/token');
}
try {
this.token = fetchToken;
}
catch(err) {
console.error(err);
}
}
}
}
一個比較常見的 Upload 組件 的應用場景,在一個頁面裏同時使用多次該組件。
<template>
<div class="upload1"><upload /></div>
<div class="upload2"><upload /></div>
</template>
就上面的代碼例子,如果使用緩存值的方法,那麼頁面一打開就會請求兩次獲取 Token 接口。
繼續完善 Upload 組件
//Upload.vue
let fetchToken = null;
export default {
methods: {
async upload() {
try {
this.fetchToken();
const token = await fetchToken;
// ...
} catch (err) {
alert(err.message);
this.refreshToken();
}
},
refreshToken() {
fetchToken = null;
this.fetchToken();
},
fetchToken() {
if (!fetchToken) {
fetchToken = request.get('/api/qiniu/token');
}
}
},
created() {
this.fetchToken();
}
};
爲了防止多個 Upload 組件 token 不同步問題,不再通過this.token
保存 token,而是每次都等待 fetchToken resolved,保證獲取到的 token 一定是最新的。
當然,這裏還有很多需要優化,例如失敗後的重試、判斷是 401 失敗才刷新 token、設置錯誤時間、定時刷新等等,但總體思路就是上面代碼所展示的內容。
另外再介紹一個經典應用場景
const fetchConfig = (() => {
let configRequest = null;
return () => {
if (!configRequest) {
configRequest = Promise.all([services.customer.config1, services.customer.config2])
.then(([data1, data2]) => {
return { data1, data2 };
})
.catch(err => {
configRequest = null;
return Promise.reject(err);
});
}
return configRequest;
};
})();
export default {
async beforeRouteEnter(to, from, next) {
try {
// 配置信息僅需要成功請求一次
const [data, config] = await Promise.all([services.customer.getInfo(), fetchConfig()]);
next(vm => {
vm.data = data;
vm.config = config;
vm.init();
};
} catch (err) {
next(err);
}
}
};