Location>code7788 >text

Taking stock of some of Vue3 watch's features that can come in handy at critical moments

Popularity:159 ℃/2024-11-21 11:35:59

preamble

The watch API should be familiar to everyone, and in the Vue3 version of the watch to add a lot of useful functions, such asThe dep option supports passing in numberspause, resume, stop methodsonce optiononCleanup functionThese features are not usually used by everyone, but they can play a big role in some specific scenarios. These features are not usually used by everyone, but in some specific scenarios, they can play a big role, this article Ouyang will take you to inventory these features.

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

deepSupport for incoming numbers

deepThe options should be familiar to you, and the common values aretrueorfalseIf you want to listen in depth, you can do so by clicking the "Listen in depth" button.watchThe incoming object.

In the Vue 3.5 release there is a change to thedeepoption has been enhanced to support not only boolean values, but also passing in numbers, which indicate the number of layers to listen on.

Take for example the following example:

const obj1 = ref({
  a: {
    b: 1,
    c: {
      d: 2,
      e: {
        f: 3,
      },
    },
  },
});

watch(
  obj1,
  () => {
    ("I'm listening in.obj1variations");
  },
  {
    deep: 3,
  }
);

function changeDeep3Obj() {
   = 20; // able to triggerwatchpull back (of a key (in music)
}

function changeDeep4Obj() {
   = 30; // cannot be triggeredwatchpull back (of a key (in music)
}

In the example abovewatch(used form a nominal expression)deepAn option value of 3 indicates listening to layer 3 of the object.

changeDeep3Objfunction is to modify the object's 3rd level of thedattribute, so it can trigger thewatchThe pullback.

(indicates contrast)changeDeep4Objfunction is to modify the object's level 4fattribute, so it can't trigger thewatchThe pullback.

His implementation is also very simple, let's look at the DEEP related source code:

function watch(source, cb, options) {
  // ...an omission
  if (cb && deep) {
    const depth = deep === true ? Infinity : deep
    getter = () => traverse(baseGetter(), depth)
  }
  // ...an omission
}

Here.depthIt means that watch listens to the depth of an object.

in the event thatdeepoption has a value of true, then thedepthSet to positive infinityInfinity, indicating the need to listen to the deepest part of the object.

in the event thatdeepoption has a value of false or is not passed into thedeep, then it indicates that only the outermost level of the object needs to be listened to.

in the event thatdeepoption has a value of type number, then assign the number to thedepth, indicating the need to listen to a specific layer of the object.

pause, resume, stop methods

These three methods were also introduced in Vue 3.5, by deconstructing thewatchThe return value of the function can then be gotten directlypauseresumestopThese three methods.

Let's look at the source code, which is actually quite simple:

function watch(source, cb, options) {
  // ...an omission
   = (effect)
   = (effect)
   = watchHandle
  return watchHandle
}

Overwatch returns a file namedwatchHandleobject, which has on top of it thepause、resume、stopThese three methods so that we can deconstruct thewatchThe return value of the function gets these three methods.

pausemethod serves to "pause" the trigger of the watch callback, i.e., its callback function will not be triggered during the pause, regardless of any changes to the responsive variables that the watch listens to.

If there is a "pause", then there must be a "resume".

resumeThe role of the method is to resume the triggering of the watch callback, at which point it will actively execute the watch callback once. Later, when the responsive variable that watch listens to changes, his callback function will also trigger.

Let's take a look at the demo, the code is as follows:

<template>
  <button @click="count++">count++</button>
  <button @click="()">pause (media player)</button>
  <button @click="()">resumption</button>
  <button @click="()">cessation</button>
</template>

<script setup lang="ts">
import { watch, ref } from "vue";

const count = ref(0);
const runner = watch(count, () => {
  ();
});
</script>

Clicking on the "count++" button will cause thewatchconsole execution in the callback.

But when we click on the "pause" button, no matter how much we click on the "count++" button, it won't trigger thewatchThe pullback.

strike (on the keyboard)resumptionbutton will be triggered once immediately after thewatchThe callback is executed, and clicking on the "count++" button later will also trigger thewatchThe pullback.

Let's see.pausecap (a poem)resumeThe source code for the method, which is very simple, is as follows:

class ReactiveEffect {
  pause(): void {
     |= 
  }

  resume(): void {
    if ( & ) {
       &= ~
      if ((this)) {
        (this)
        ()
      }
    }
  }

  trigger(): void {
    if ( & ) {
      (this)
    } else if () {
      ()
    } else {
      ()
    }
  }
}

existpauseresumemethod by modifying theflagsproperty to toggle whether it is "paused" or not.

in implementingtriggerWhen the method dependency is triggered, it goes ahead and reads theflagsattribute determines if the current state is "paused", and if so, the watch callback is not executed.

As you can see from the code above these three methods are in theReactiveEffectclass above, thisReactiveEffectclass is an underlying class of Vue.watchwatchEffectwatchPosEffectwatchSyncEffectare all implemented based on this class, so naturally they support thepauseresumestopThese three methods.

endstopmethod, and when you're sure you don't want to trigger the watch callbacks anymore, call thestopmethod. The code is as follows:

const watchHandle: WatchHandle = () => {
  ()
  if (scope && ) {
    remove(, effect)
  }
}

 = watchHandle

responsive variablecountThe collected set of subscribers has this watch callback, so when thecountwill trigger the watch callback when its value changes. Here thestopmethod relies heavily on a two-way linked list to pull this watch callback from the responsive variablecountis removed from the subscriber collection, so after executing the stop method whatever thecountHow the value of the variable changes, the watch callback will not be executed anymore. (PS: If you can't understand this paragraph, I suggest you go check out my last postVue3.5 Bidirectional Chained Tables(Article, read it and you'll understand)

once option

If you only want your watch callbacks to execute once, then try thisonceoption, which is new in Vue 3.4.

Look at a demo:

<template>
  <button @click="count++">count++</button>
</template>

<script setup lang="ts">
import { watch, ref } from "vue";

const count = ref(0);
watch(
  count,
  () => {
    ("once", );
  },
  {
    once: true,
  }
);
</script>

As a result of the use ofonceoption, so only the first click on the "count++" button will trigger the watch callback. Any subsequent clicks on the button will not trigger the watch callback.

Let's see.onceThe source code for the option, which is very simple, is as follows:

function watch(source, cb, options) {
  const watchHandle: WatchHandle = () => {
    ()
    if (scope && ) {
      remove(, effect)
    }
  }

  if (once && cb) {
    const _cb = cb
    cb = (...args) => {
      _cb(...args)
      watchHandle()
    }
  }

  // ...an omission
   = (effect)
   = (effect)
   = watchHandle
  return watchHandle
}

Let's look at the code in the middle firstif (once && cb)The phrase means that ifonceoption has a value of true and the watch callback is also passed in. Then encapsulate a new layer ofcbcallback function, which still executes the watch callback passed in by the user in a new callback function. It then goes on to execute awatchHandlefunction, thiswatchHandleDoesn't that look familiar?

aforementionedstopmethod is actually executing thiswatchHandleAfter executing thiswatchHandlefunction, watch no longer listens to thecountvariable is up, so it doesn't matter if the subsequentcountHow the variable is modified, the watch callback will not be triggered again.

onCleanup function

There are situations where we need watch to listen on a variable and then go to initiate a http request. If the variable changes quickly, the second request will be initiated before the first request comes back. In some extreme cases, the response to the first request will be slower than the response to the second request, and the return value from the first request will overwrite the return value from the second request. In fact, we expect to get the return value of the second request in the end.

In this case we can use theonCleanup function, who is exposed to us as the third parameter of the watch callback. Look at an example:

watch(id, async (newId, oldId, onCleanup) => {
  const { response, cancel } = myFetch(newId)
  // When `id` changes, `cancel` will be called.
  // cancel the previous outstanding request
  onCleanup(cancel)
   = await response
})

The first two parameters of the watch callback are familiar: the new id value and the old id value. The third parameter is theonCleanupfunction, which is called before the watch callback is triggered, so we can use him to cancel the last request.

onCleanupThe registration of the function is also simple with the following code:

let boundCleanup

boundCleanup = fn => onWatcherCleanup(fn, false, effect)

function watch(source, cb, options) {
  // ...an omission
  const job = (immediateFirstRun?: boolean) => {
    const args = [
      newValue,
      oldValue,
      boundCleanup,
    ]
    cb(...args)
    oldValue = newValue
  }
  // ...an omission
}

Executing the watch callback is actually executing thisjobfunction in thejobThree parameters are passed in to the function to execute the watch callback. These arenewValueoldValueboundCleanup. The first two parameters are familiar to everyone, and the third parameterboundCleanupis a function:fn => onWatcherCleanup(fn, false, effect)

this oneonWatcherCleanupAre you all familiar with this? This is also an API exposed by Vue, registering a cleanup function to be executed when the current listener is about to be re-run. There's a lot of information about theonWatcherCleanupPreviously Ouyang wrote an article dedicated to how to use it:Wrapping the auto-cancel fetch function with Vue 3.5's onWatcherCleanup

summarize

This post takes stock of some of the new features added to Vue3 watch:The dep option supports passing in numberspause, resume, stop methodsonce optiononCleanup functionThe following are some of the features that you may not normally use. These are features that you may not normally use, but it is still important to know that they are there because there are situations where they can come in handy.

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.