Location>code7788 >text

Wrapping the auto-cancel fetch function with Vue 3.5's onWatcherCleanup

Popularity:5 ℃/2024-09-11 09:10:05

preamble

In Ouyang's last postThis should be the most detailed explanation of Vue 3.5 on the net!The article has a number of students who are interested in Vue 3.5's newonWatcherCleanupIt's a bit confusing, this added API seems to be the same as thewatch APIThe third parameter of the callbackonCleanupThe functionality seems to be duplicated. Today's post is about the addition of theonWatcherCleanupScenarios for the use of functions:Wrapping a fetch function for automatic canceling

Follow the public number: [Front-end Ouyang], give yourself a chance to advance vue

The third parameter of the watch callback, onCleanup.

Some students may not be awarewatchThe third parameter of the callbackonCleanupWe'll start with a demo, the code is as follows:

watch(id, (value, oldValue, onCleanup) => {
  ("do something");
  onCleanup(() => {
    ("cleanup");
  });
});

watchThe first two parameters of the callback should be familiar to you, which arevalueThe new value.oldValueOld value.

Third parameteronCleanupYou may not usually use it much, it's a callback function, when thewatchis executed after the value of the component changes or before the component is destroyed.onCleanupincoming callbacks.

In the demo above is the variableidTriggered on changeonCleanupThe pullback in, and thusconsoleprintable"cleanup"string. Or maybe the component it's in triggers before it's destroyed.onCleanupThe pullback in, and thusconsoleprintable"cleanup"String.

Then we're inonCleanupWhat can I do in it?

The answer is that it's possible to clean up side effects, such as in overwatch withsetIntervalInitialize a timer. Then we can initialize the timer in theonCleanupcallback to clean up the timer without having to go to the component'sbeforeUnmountThe hook function goes to unify the cleanup.

onWatcherCleanupfunction (math.)

onWatcherCleanupThe role of the function is the same as that of thewatchThe third parameter of the callbackonCleanupPretty much, also whenwatchis executed after the value of the component changes or before the component is destroyed.onWatcherCleanupincoming callbacks.

The usage is also very simple, the code is as follows:

import { watch, onWatcherCleanup } from "vue";

watch(id, () => {
  ("do something");
  onWatcherCleanup(() => {
    ("cleanup");
  });
});

As you can see from the code aboveonWatcherCleanupThe usage is actually similar to that ofwatchThe third parameter of the callbackonCleanupPretty much the same, the difference is that hereonWatcherCleanupis imported from vue.

In addition to the difference of importing from vue, there's also a difference betweenonWatcherCleanupnot just inwatchcan be used in thewatchEffectThe same can be used in For example, something like the following:

watchEffect(() => {
  ("do something in watchEffect", );
  onWatcherCleanup(() => {
    ("cleanup watchEffect");
  });
});

As in the previous example, the code aboveidor when the component is destroyed.onWatcherCleanupfunction in thePrint.

onWatcherCleanupfunction is imported from vue, then this means that theonWatcherCleanupFunction calls can be written anywhere, as long as they end up in the function's hierarchy of calls still in thewatchorwatchEffectin the callback is fine.

Using this feature above we can use theonWatcherCleanuptouch baseonCleanupThings that can't be done, e.g., encapsulating an automatedcancel(used form a nominal expression)fetchfunction.

Wrapping the fetch function for autocancel

Before we get to that let's find out how tocancelanfetchfunction.

Here's what's involvedAbortControllerInterface.AbortController Interface represents a controller object that allows you to abort one or more Web requests as needed.

Here's one.cancelCancel a request demo with the following code:

const controller = new AbortController();
const res = await fetch(url, {
  ...options,
  signal: ,
});

setTimeout(() => {
  ();
}, 500);

first usingnew AbortController()Create a controller objectcontroller

includedReturns aAbortSignal object instance that can be used to communicate with or abort an asynchronous operation.

In our case putact assignaloption is passed directly to the fetch function.

Finally, there is the possibility of using()Cancel the fetch request, in the above demo it is if the request is not completed for more than 500ms, then execute the()Cancel the fetch request out.

With this knowledge in mind, let's take a look at the use of the "Automaticcancel(used form a nominal expression)fetchfunction", the code is as follows:

<script setup lang="ts">
import { watch, ref, watchEffect, onWatcherCleanup } from "vue";
import myFetch from "./myFetch";

const id = ref(1);
const data = ref(null);

watch(id, async () => {
  const res = await myFetch(`http://localhost:3000/api/${}`, {
    method: "GET",
  });
  (res);
   = res;
});
</script>

<template>
  <p>data is: {{ data }}</p>
  <button @click="id++">id++</button>
</template>

In the above example use thewatchListened to the variableid, in the callback of the listener will use the encapsulatedmyFetchFunction request interface.

The above example should be often encountered by you in your daily life, if theidvalue changes quickly, but the server-side interface request takes 2 seconds to complete, at which point we expect that only the lastidThe requests triggered by a change in the value of the

If there is a change in themyFetchThe component is destroyed during the request, at which point we also expect the request cancel to be canceled.

Before Vue 3.5 it was a pain in the ass to try to implement these two requirements above, but with Vue 3.5'sonWatcherCleanupIt is very easy after the function.

This one is encapsulated automaticallycancel(used form a nominal expression)fetchfunction.The file code is as follows:

import { getCurrentWatcher, onWatcherCleanup } from "vue";

export default async function myFetch(url: string, options: RequestInit) {
  const controller = new AbortController();
  if (getCurrentWatcher()) {
    onWatcherCleanup(() => {
      ();
    });
  }

  const res = await fetch(url, {
    ...options,
    signal: ,
  });

  let json;
  try {
    json = await ();
  } catch (error) {
    json = {
      code: 500,
      message: "JSON format error",
    };
  }
  return json;
}

due toonWatcherCleanupfunction is imported from vue, then we can add it to our own wrappedmyFetchfunction to import and use him.

existonWatcherCleanupfunction's callback we execute the()As mentioned earlier whenwatchorwatchEffectbefore the callback is executed or before the component is uninstalled.onWatcherCleanupRegistered callbacks. What we have here is themyFetchYes, it is.watchwhich, of course, triggers a call to theonWatcherCleanupRegistered callbacks.

existonWatcherCleanupis executed in the callback of the()We talked earlier about the implementation of()It will then cancel the fetch function being requested to cancel.

It's as simple as that to fulfill the first two requirements:

Demand One:in the event thatidvalue changes quickly, but the server-side interface request takes 2 seconds to complete, at which point we expect that only the lastidThe requests triggered by a change in the value of theHere's a gif of the variable id being modified multiple times in a short period of time:
click

As you can see from the gif above only the last request was completed, all other requests were canceled.

Demand II:If there is a change in themyFetchThe component is destroyed during the request, at which point we also expect the request cancel to be canceled.Here's a gif of the component uninstalled:
hide

As you can see from the above figure, the component is requesting data from the server when it is uninstalled, and the request is automatically canceled at this point.

The attentive little ones have found a great deal of interest in themyFetchfunction.onWatcherCleanupfunction is wrapped around agetCurrentWatcherjudgment, the code is as follows:

import { getCurrentWatcher, onWatcherCleanup } from "vue";

export default async function myFetch(url: string, options: RequestInit) {
  // ...an omission
  if (getCurrentWatcher()) {
    onWatcherCleanup(() => {
      ();
    });
  }
  // ...an omission
}

When the value of the watch or watchEffect listener has changedonWatcherCleanupcallback is triggered, so theonWatcherCleanupis triggered by the watch or watchEffect in which it is executed.

in the event thatonWatcherCleanupis not executed in the watch or watchEffect callback, then of course theonWatcherCleanupThe callbacks in will also never be executed.

Some of you may have questions about what you have here.onWatcherCleanupYes, it is.myFetchand not in the watch or watchEffect callbacks?

The answer.myFetchfunction is executed in watch.myFetchThen go ahead and executeonWatcherCleanup

(indicates contrast)getCurrentWatcher()The function then returns the currentCallbacks being executedor watchEffect, if the currentmyFetchis not executed in the watch or watchEffect callback, then thegetCurrentWatcher()The return value of the function is null, so there's no need to perform theonWatcherCleanupfunction now.

Lastly, it's worth mentioning thatonWatcherCleanupIt can't be executed after await, like in the following code:

import { getCurrentWatcher, onWatcherCleanup } from "vue";

export default async function myFetch(url: string, options: RequestInit) {
  const controller = new AbortController();
  const res = await fetch(url, {
    ...options,
    signal: ,
  });

  let json;
  try {
    json = await ();
  } catch (error) {
    json = {
      code: 500,
      message: "JSON format error",
    };
  }
  // ❌ Incorrectly written
  if (getCurrentWatcher()) {
    onWatcherCleanup(() => {
      ();
    });
  }

  return json;
}

In the code above we've set theonWatcherCleanupThe call is placed in theawait fetch()This way of writingonWatcherCleanupregisteredCallbacks are not executed

Why is it in theawaitbackonWatcherCleanupWhat about registered callbacks that never execute?

The answer is that js's await is equivalent to registering a callback function to execute the post-await code, and then executing that callback function when the await wait is over, thus executing the post-await code.

await and the code before it is indeed executed in the watch callback, where we have theonWatcherCleanupIt is the code after the await, the code after the await is executed in a new callback, that is, the watch "callback" in the "callback" execution.

(coll.) fail (a student)onWatcherCleanupexecution no longer knows who the currently executing watch callback is, so theonWatcherCleanupThe callbacks are also not registered. When a variable in watch is modified or when a component is uninstalled theonWatcherCleanupRegistered callbacks are never executed.

summarize

(coll.) fail (a student)watchorwatchEffectlistener's variables are modified, and when the component is uninstalled, it goes to execute their callbacks using theonWatcherCleanupregistered callback function. AndonWatcherCleanupis imported from vue, making mine executable anywhere!onWatcherCleanupfunction. Using these two features we can encapsulate a fetch function that automatically cancels.

Follow the public number: [Front-end Ouyang], give yourself a chance to advance vue

Also Ouyang wrote an open source ebookvue3 Compilation Principles Revealed, reading this book can give you a qualitative improvement in your knowledge of vue compilation. This book is accessible to beginner and intermediate front-ends and is completely free, just asking for a STAR.