老板又来了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???)

这咋整!!!?

通过一番强烈的探讨,砸头锤脑版的洗礼,咱们终于得出了一个计划,咱们筹备:

  1. 让没有申请胜利的申请信息先保留到本地,而后再距离肯定工夫
  2. 离线状态下间接将申请信息保留到本地,而后在联网的状况下再提交

完满!!!

// 代码有点长,自行脑补...

SB老板又来了

老板:哈哈,如同比之前是好多了,能不能把积分性能、皮肤性能、订单性能都用上你的缓存计划。

我:

老板:哦对了,后续咱们还要搞pad版...

好吧,没辙了,究竟还是没有逃过封装这一步。

咱们决定封装成一个库

前端组长:哦,辛苦你啦,这事就交给你负责吧。既然要做成一个js库,那咱们就得好好考虑一下这些问题了。

  1. 要如何形象能力笼罩更多场景?
  2. 能不能让前端老手也能很快上手?
  3. 申请相干的状态多且繁冗,能不能也对立治理起来?
  4. 公司后续可能会有react我的项目,能不能一起兼容?
  5. 写进去的库不能太大

我:好的组长,这对我也是一次挑战,我现申请两个月工夫好好设计一下能够吗?

前端组长:好的,去干吧小伙子!!!

不过这真的很煎熬,很有挑战,要反对那么多条件。通过了一段时间的挠头思考,我失去的后果是:头上越来越凉了,不过,还是要持续!!!

我想出了一个申请场景治理的概念

通过我茶不思饭不想的加紧奋斗,我终于交出了一稿,我想出了一个 申请场景治理 的概念。什么是申请场景治理呢?大略是这样的。

咱们在进行一次申请时总是会遇到这些问题:

  1. 什么时候发出请求;
  2. 是否要展现申请状态;
  3. 是否要封装成申请函数以便反复调用;
  4. 要如何加工响应数据;
  5. 是否要对高频应用的响应数据做缓存;
  6. 如何进行跨页面操作数据;
  7. 离线了还能提交数据吗;
  8. ...

fetchaxios往往更专一于如何与服务端交互,但对于下面的问题咱们总是须要本人解决,这些有利于利用性能和稳定性的性能,总会让程序员们编写出低维护性的代码。申请场景治理就是从筹备申请到响应数据加工结束的所有环节进行形象,从而笼罩以前端为视角的,整个CS交互生命周期的模型。

CS交互:泛指所有客户端类型和服务端的数据交互

来,间接上模型图。

alova诞生了

按着申请场景治理的逻辑,咱们实现了这个js库,名叫alova,它就像一个申请库的武装盔甲,帮忙咱们应用申请库发动申请,同时以响应式状态的模式来治理申请相干的数据,咱们对它的定位是对axios等申请库的一种补充,而非替代品。

哈哈,这些我都做到啦!!!

  1. ✅形象笼罩更多场景
  2. ✅axios类似的api,前端老手也能很快上手
  3. ✅静默提交、离线提交
  4. ✅将申请相干的各种状态都对立治理起来了
  5. ✅兼容公司以后的vue我的项目,同时也兼容公司后续的react我的项目
  6. ✅压缩后3+kb

当然性能还远不止于此!!!还有这些:

  1. ✅申请的非异步模式
  2. ✅响应数据缓存
  3. ✅数据预拉取
  4. ...

alova库传送门在此!!!,求你不要不识好歹,点个start吧

而后咱们的todo列表展现就能够改成这样了。
先创立一个alova实例,是不是很像创立一个axios实例

// api/index.jsexport 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.vueconst 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吧