共计 4685 个字符,预计需要花费 12 分钟才能阅读完成。
老板又来了 BT 的需要😳
咱们公司最近开发一款 todo 治理产品,vue3+axios,事件是这样的。
老板:咱们的产品测试版我试用了,整体实现的不错,不过……
我:😨😨😨
老板:体验上有待优化一下,比方……todo 列表页翻页的时候能不能霎时展现数据?增加和编辑 todo 的时候可不可以不要期待了?
我(心想):❓❓❓这是要跳过和服务器交互阶段?把我给整不会了!
老板:小伙子,看好你哦,今天能够出一版我看看吗?
我:我 … 我试试!
我忽然伶俐一闪
针对翻页霎时展现数据的问题,咱们是不是能够先申请当前页数据,而后做一个缓存来预加载下一页数据,说干就干,代码大略是这样的:
// todo 列表组件
const currentPage = ref(1);
const pageCache = {};
const loading = ref(false);
// 如果有缓存则返回缓存,没有则申请数据并缓存
const getPageData = async page => {let pageData = pageCache[page];
if (!pageData) {
loading.value = true;
pageData = await axios.get('...', {
params: {
page: page,
pageSize: 10,
}
});
loading.value = false;
pageCache[page] = pageData;
}
return pageData;
};
// 进入时申请当前页数据,同时预加载下一页数据
const todoList = ref([]);
const loadNextPage = async () => {const pageData = await getPageData(currentPage.value);
todoList.value.push(pageData);
currentPage.value++;
getPageData(currentPage.value);
};
onMounted(loadNextPage);
window.addEventListener(() => {if (/* 判断滑动到最底部... */) {loadNextPage();
}
});
针对增加和编辑 todo 也不期待的问题,把更新事件传过来,而后我间接乐观更新
// 编辑 todo 组件
const addTodo = newTodoData => {axios.post('...', newTodoData).then().then(newId => {
// 响应后将原 todo 项替换为带 id 的项
this.$emit('replaceTodo', {
search: newTodoData,
replacement: {
id: newId,
...newTodoData,
}
});
});
// 先立刻增加到 todoList
this.$emit('addTodo', newTodoData);
alert('提交胜利');
}
如同所有都那么完满,老板该要夸我了。
我真的 emo 了😓
老板:小伙子,干的不错,只是 ….
我:😲😲😲
老板:我方才网络不太好,显示提交胜利了,但如同又没有真正胜利,不太稳啊。。。
我:好的老板,我再回去看看😔😔😔
老板:对了,如果能在没有网络的状况下也能应用就更好了。
我:好的老板!😀😀😀(心里:What???🤬🤬🤬)
这咋整!!!?
通过一番强烈的探讨,砸头锤脑版的洗礼,咱们终于得出了一个计划,咱们筹备:
- 让没有申请胜利的申请信息先保留到本地,而后再距离肯定工夫
- 离线状态下间接将申请信息保留到本地,而后在联网的状况下再提交
😎😎😎完满!!!
// 代码有点长,自行脑补...
SB 老板又来了
老板:哈哈,如同比之前是好多了,能不能把积分性能、皮肤性能、订单性能都用上你的缓存计划。
我:🤢🤢🤢
老板:哦对了,后续咱们还要搞 pad 版 …
💀💀💀好吧,没辙了,究竟还是没有逃过封装这一步。
咱们决定封装成一个库
前端组长:哦,辛苦你啦,这事就交给你负责吧。既然要做成一个 js 库,那咱们就得好好考虑一下这些问题了。
- 要如何形象能力笼罩更多场景?
- 能不能让前端老手也能很快上手?
- 申请相干的状态多且繁冗,能不能也对立治理起来?
- 公司后续可能会有 react 我的项目,能不能一起兼容?
- 写进去的库不能太大
我:好的组长,这对我也是一次挑战,我现申请两个月工夫好好设计一下能够吗?🤨🤨🤨
前端组长:好的,去干吧小伙子!!!😚😚😚
不过这真的很煎熬,很有挑战,要反对那么多条件。通过了一段时间的挠头思考,我失去的后果是:头上越来越凉了😶😶😶,不过,还是要持续!!!
我想出了一个申请场景治理的概念
通过我茶不思饭不想的加紧奋斗,我终于交出了一稿,我想出了一个 申请场景治理 的概念。什么是申请场景治理呢?大略是这样的。
咱们在进行一次申请时总是会遇到这些问题:
- 什么时候发出请求;
- 是否要展现申请状态;
- 是否要封装成申请函数以便反复调用;
- 要如何加工响应数据;
- 是否要对高频应用的响应数据做缓存;
- 如何进行跨页面操作数据;
- 离线了还能提交数据吗;
- …
而 fetch
或axios
往往更专一于如何与服务端交互,但对于下面的问题咱们总是须要本人解决,这些有利于利用性能和稳定性的性能,总会让程序员们编写出低维护性的代码。申请场景治理就是从筹备申请到响应数据加工结束的所有环节进行形象,从而笼罩以前端为视角的,整个 CS 交互生命周期的模型。
CS 交互:泛指所有客户端类型和服务端的数据交互
来,间接上模型图。
alova 诞生了
按着申请场景治理的逻辑,咱们实现了这个 js 库,名叫 alova
,它就像一个申请库的武装盔甲,帮忙咱们应用申请库发动申请,同时以响应式状态的模式来治理申请相干的数据,咱们对它的定位是对axios
等申请库的一种补充,而非替代品。
哈哈,这些我都做到啦!!!🤣🤣🤣
- ✅形象笼罩更多场景
- ✅axios 类似的 api,前端老手也能很快上手
- ✅静默提交、离线提交
- ✅将申请相干的各种状态都对立治理起来了
- ✅兼容公司以后的 vue 我的项目,同时也兼容公司后续的 react 我的项目
- ✅压缩后 3 +kb
当然性能还远不止于此!!!还有这些:
- ✅申请的非异步模式
- ✅响应数据缓存
- ✅数据预拉取
- …
alova 库传送门在此!!!,求你不要不识好歹,点个 start 吧🤣🤣🤣
而后咱们的 todo 列表展现就能够改成这样了。
先创立一个 alova 实例,是不是很像创立一个 axios 实例
// api/index.js
export const alovaInstance = createAlova({
baseURL: 'https://api.alovajs.org',
// vue 我的项目传入 VueHook,react 我的项目传入 ReactHook
statesHook: VueHook,
// 传一个申请适配器,GlobalFetch 是咱们提供的 fetch api 适配器
// 你想用 axios 也能够自定义一个适配器
requestAdapter: GlobalFetch(),
// 是不是有相熟的滋味
beforeRequest(config) {config.headers.token = 'tokenxxx';},
async responsed(response) {const json = await response.json();
if (json.code !== 200) {throw new Error(json.message);
}
return json.data;
},
});
定义申请函数。
// api/todo.js
// 创立申请对象
export const getTodoList = page => alovaInstance.Get('...', {
params: {
page,
pageSize: 10,
},
localCache: 50000,
});
最初在组件中发动申请。
// TodoList.vue
const currentPage = ref(1);
const todoList = ref([]);
// 创立预加载器
const {fetch} = useFetcher();
const {
loading,
data: pageData,
error,
onSuccess,
// 监听 currentPage 变动就去触发申请
} = useWatcher(() => getTodoList(currentPage.value), [currentPage], {immediate: true});
onSuccess(rawPageData => {todoList.value.push(rawPageData);
// 申请胜利后预加载下一页数据,并缓存
fetch(getTodoList(currentPage.value + 1));
});
window.addEventListener(() => {if (/* 判断滑动到最底部... */) {
// 页码扭转,主动发动申请,而后命中缓存并立刻调用 onSuccess
currentPage.value ++;
}
});
不必期待网络的 todo 项创立是这样的,开启了 silent 模式后,离线状态下照样能够失常实现哦。
创立 todo 的申请函数定义
// api/todo.js
// 创立申请对象
export const createTodo = newTodo => alovaInstance.Post('...', newTodo);
点击创立按钮后进行静默提交。
const newTodo = reactive({
title: '',
time: '',
});
const {
send: requestCreateTodo
onSuccess
} = useRequest(() => createTodo(newTodo), {
// 设置不立刻发出请求,而是改用 send 函数调用发动,即手动模式
immediate: false,
// 设置为静默提交模式
silent: true,
});
// 静默提交时,胜利回调将会被立刻执行
onSuccess(() => {
// 在这里手动更新新的 todo 项到 todoList 里
updateState(getTodoList(), todoList => {
return [
...todoList,
{
// 看到这了没?🤩🤩🤩,这是提早更新数据的写法
'+id': resData => resData.id,
...newTodo,
}
]
});
});
// 假如点击“创立”按钮后触发此函数
const handleCreateTodoBtnClick = () => {requestCreateTodo();
};
这样也就实现了静默提交的操作了,图中有个 提早更新数据 的性能我要特地阐明一下(划重点🖍🖍🖍),它就像一个占位符,既能够让已确定的数据立刻更新到对应的响应式状态中,让界面立刻从新渲染,又能够在稍后申请响应后将占位符替换为理论数据的。
例子中就是在创立 todo 项时立刻将 todo 项更新到 todo 列表数据中,同时它的 id 将在提交胜利后主动替换为理论的 id,这样就做到了非提早的数据提交。
😃这次,咱们老板终于开心了!
而后,咱们又花了一些工夫将我的项目的多个端应用 alova
革新,都获得了不错的成果,咱们都泣不成声了!!!尤其是老板🧔🧔🧔。
老板:小伙子,干的丑陋,我要给你颁发奖章啊,咱们的产品体验相比之前回升了一个品位了啊!
我:😶😶😶老板,别激动!!!要不 … 给我多发个两万奖金就行啦 …
老板:你在想屁吃!!!😲😲😲
前端组长:你在想屁吃!!!😲😲😲
我:开玩笑开玩笑,组长让我当就行了🤭🤭🤭
前端组长:我 …qnmlgb!
各位看官们,你们感觉这个想法如何呢?让我看到你们的双手🙌🏻🙌🏻🙌🏻
再来一遍,alova 库传送门在此!!!,求你不要不识好歹,点个 start 吧🤣🤣🤣