网络请求的往往是限制前端新能的重要因素,页面的卡顿往往不是前端渲染的问题而是请求数据慢或者频繁请求导致的。大型复杂的业务更加需要做请求的缓存,同时又要有细粒度的控制能力,哪些请求可以缓存,哪些请求不需要缓存,哪些请求可以直接存到localStorage里,哪些只在当前页面做缓存,以及何时应该清理缓存。为了解决以上问题,我们自己封装了一个强大的createService方法,可以高效,便捷,细粒度去控制组件的请求,并且避免相同的请求重复执行,但在做缓存的同时一定要注意是不是真的需要做缓存,数据是否有时效性的要求。
下面直接上代码,沉淀了两年的createService终于出炉了(其中的一些依赖(request,Storage)就不罗列了,理解设计思想就可以),最终目的就是创建一个mixin
export const createService = (defaultOptions = {}) => {
const {
key,
url,
parser = null,
cache = false,
vuex = false,
param: defaultParam = {},
getParam = (instance) => ({})
} = defaultOptions;
let store;
cache && (store = Storage.get(`${key}_${_global.version}`));
store = store || { ...serviceObj };
// clear, 这块代码只会执行一次,所以退出登录后不应该吧cbs清空
!cache && serviceManager.add(() => {
store = { ...serviceObj };
});
let loadPromise;
return {
[key]: (userOptions = {}) => {
const options = { ...defaultOptions, ...userOptions };
// 具体看哪些参数可以被重置的
const { autoLoad = true } = options;
// 方法首字母大写
const strFn = key.charAt(0).toUpperCase() + key.slice(1);
const loadKey = `load${strFn}`;
const clearKey = `clear${strFn}`;
const loadingKey = `loading${strFn}`;
return {
data() {
return {
[key]: [],
[loadingKey]: false
};
},
created() {
autoLoad && (this[loadKey + 'Promise'] = (this[loadKey]({ ...defaultParam, ...getParam(this) })));
},
methods: {
[loadKey](param, opts = {}) { // eslint-disable-line
this[loadingKey] = true;
let ajax = vuex ? this.request : this.$request;
const localData = serviceCompare(param, store);
const wrapPromise = (p) => {
return p.then((res) => {
res.parsedData = res.parsedData || (parser && parser(res.data));
store = {
param,
res,
};
this[key] = store.res.parsedData || store.res.data;
typeof cache === 'function' ? cache(key, store) : cache && Storage.set(`${key}_${_global.version}`, store);
return res;
}).catch((res) => {
console.error(res);
this.$Message.error(res.msg);
return Promise.reject(res);
}).finally(() => {
this[loadingKey] = false;
});
};
if (!localData && loadPromise && isEqualWith(store.param, param)) {
return wrapPromise(loadPromise);
}
loadPromise = ajax({
url, // 必须是mutationType
type: 'GET',
localData,
loading: false,
param,
...opts
});
return wrapPromise(loadPromise);
},
[clearKey]() {
store = { ...serviceObj };
loadPromise = undefined;
this[loadingKey] = false;
}
}
};
}
};
};
使用方式
定义service(此处的initTree可以看我的另一篇树结构处理的博客,个人觉得还是不错的)
const services = {
// 手机国际区号
...createService({
key: "mobilePrefix",
cache: true,
url: '_COMMON_MOBILE_PREFIX_GET',
}),
// 全国所有的地址
...createService({
key: "region",
url: '_COMMON_PUBLIC_ALL_GET',
cache: true,
parser: (data) => initTree(data, { labelKey: 'region_name', valueKey: 'national_code', childrenKey: 'children' })
}),
// 物流公司
...createService({
key: "logisticsCompany",
url: '_COMMON_PUBLIC_EXPRESS_ITEMS_GET',
cache: true
})
};
调用service
import service from '@stores/services/common';
export default {
name: 'xls-filter',
components: {
},
mixins: [service.region()]
....
}