Location>code7788 >text

Holy crap, vue3 components can even "pause" rendering!

Popularity:205 ℃/2024-08-19 08:43:25

preamble

There are times when we want toAfter getting the data from the serverAnd then to render a component, in order to achieve this effect we currently have several implementations:

  • Put the data request to the parent component to do it and use thev-ifThe control gets the child component before rendering it, and then passes the data from the parent component through thepropsPassed to the subcomponent.

  • In the subcomponent'sonMountedand request the data using thev-ifIn the subcomponent'stemplateThe outermost layer is controlled and only renders the content in the subcomponent when it gets the data.

Both of the above options have their own drawbacks and are not perfect. The ideal solution would be to put the logic for fetching data from the server in a subcomponent, and during the fetching period let the subcomponent"Pause."For a moment, don't render until the data request is complete and then go ahead and render the subcomponent for the first time.

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.

The perfect solution

The disadvantage of the first approach is that although the child component gets the data before it starts rendering, the logic of the data request is put on top of the parent component, and we expect all the logic to be encapsulated inside the child component.

The disadvantage of the second approach is that it actually renders the subcomponent once on initialization, when we haven't got the data from the server yet. That's why we have to use thev-ifexisttemplateThe outermost control of the subcomponent is not rendered at this time. When you get the data from the server and then render the subcomponent a second time, only then will the content in the subcomponent be rendered to the page.This method obviously subcomponents are rendered 2 times.

So is there a perfect solution where the logic to fetch the data from the server is placed in a subcomponent and during the fetching of the data the subcomponent is allowed to"Pause."What about one, not rendering it first and waiting until the data request is complete before going to render the subcomponent for the first time?

The answer is: of course you can, vue3'sSuspense component+Use await at the top of setup to fetch dataIt would be perfect for this!!!!

Two imperfect examples

To give you a better visualization of the oxymoron of the perfect solution, let's take a look at the two less-than-perfect examples we talked about earlier.

Example of requesting data in a parent component

The following is an example of requesting data in a parent component with the following code:

<template>
  <ChildDemo v-if="user" :user="user" />
  <div v-else>
    <p>loading...</p>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from "vue";
import ChildDemo from "./";

const user = ref(null);

async function fetchUser() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({
        name: "John Doe",
        phone: "13800138000",
      });
    }, 2000);
  });
}

onMounted(async () => {
   = await fetchUser();
});
</script>

The code for the subcomponent is as follows:

<template>
  <div>
    <p>user ID:{{ }}</p>
    <p>cell phone number:{{ }}</p>
  </div>
</template>

<script setup lang="ts">
const props = defineProps(["user"]);
</script>

For this scenario we will get from the server sideuserlogic is all placed in the parent component, and using thepropscommander-in-chief (military)userPassed to the subcomponent and displays a loading copy during the data retrieval from the server.

This accomplishes what we need but puts the subcomponent in the position of getting theuserlogic into the parent component, we expect to encapsulate all of this logic in the child component, so this solution is not perfect.

Example of a subcomponent requesting data in onMounted

Let's take a look at the second option, the parent component code code is as follows:

<template>
  <ChildDemo />
</template>

<script setup lang="ts">
import ChildDemo from "./";
</script>

The subcomponent code is as follows:

<template>
  <div v-if="user">
    <p>user ID:{{ }}</p>
    <p>cell phone number:{{ }}</p>
  </div>
  <div v-else>
    <p>loading...</p>
  </div>
</template>

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

const user = ref(null);

async function fetchUser() {
  // utilizationsetTimeoutSimulate getting data from the server
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({
        name: "John Doe",
        phone: "13800138000",
      });
    }, 2000);
  });
}

onMounted(async () => {
   = await fetchUser();
});
</script>

We put the data request in theonMountedin which initialization goes to the first rendering of the subcomponent. At this pointuserThe value of thenull, so we had to do it intemplateThe outermost layer of thev-if="user"Controls the content of the subcomponent not to be displayed at this time, in thev-elsein to render loading copy.

When the data is gotten from the server and given to the responsive variableuserRe-assigning the value triggers a re-rendering of the page, at which point a second rendering is performed before the content of the sub-component is rendered to the page.

As you can see from the above this solution the subcomponent is obviously rendered twice, and we also write the loaded display logic inside the subcomponent, which increases the complexity of the subcomponent code. So this solution is not perfect.

The perfect scenario would be in thefetchUserLetting subcomponents during"Pause" renderingfallbackto render a loading page. And this loading display logic doesn't need to be encapsulated in a subcomponent, it's in the"Pause" renderingperiod of timeautomationIt will be able to display it. Wait until the request for data from the server is complete before you start rendering the subcomponents and automatically unload the loading page.

Suspense + await implements the perfect example

Here's the official website for theSuspenseThe presentation:

<Suspense> is a built-in component to coordinate the handling of asynchronous dependencies in the component tree. It allows us to wait for multiple nested asynchronous dependencies at the upper level of the component tree to finish resolving at the lower level, and can render a loaded state while waiting.

What it means isSuspensecomponent can listen to the following asynchronous subcomponent and go ahead and render a loading page before waiting for the asynchronous subcomponent to finish rendering.

SuspenseThe component supports two slots:#default cap (a poem)#fallback. If#defaultslot has an asynchronous component, then it goes ahead and renders the#fallbackin the#fallbackin the page to be taken out, instead rendering the content of the asynchronous component to the page.

If our subcomponent is an asynchronous component, then theSuspenseNot just to help us realize the function we want aca.

Suspensecan be used during the loading of asynchronous subcomponents with the#fallbackThe slot automatically renders a loading loading for us, and waits until the asynchronous subcomponent is loaded before it goes to render the content in the subcomponent for the first time.

So now the question is how do we make our subcomponent an asynchronous subcomponent?

The answer to this question is actually told on the vue website, if a component's<script setup>The top layer uses theawaitThen the component becomes an asynchronous component. All we need to do is to use await at the top level of the subcomponent to request data from the server.

Parent component of the perfect solution

Here's an example of how to use theSuspenseThe code of the modified parent component, is as follows:

<template>
  <Suspense>
    <AsyncChildDemo />
    <template #fallback>loading...</template>
  </Suspense>
</template>

<script setup lang="ts">
import AsyncChildDemo from "./";
</script>

in the parent component using theSuspensecomponent, 2 slots are passed to this component.#defaultSlots are asynchronous subcomponentsAsyncChildDemoThe default slots can be used without adding the#default

And with the use of#fallbackslot, the asynchronous subcomponent will not be rendered for a while while the asynchronous subcomponent is being loaded.AsyncChildDemo. Change to render first#fallbackThe loading in the slot will automatically replace the loading with the content in the subcomponent when the asynchronous subcomponent finishes loading.

Subcomponents of the Perfect Solution

Here's one that uses theawaitThe modified subcomponent code, is as follows:

<template>
  <div>
    <p>user ID:{{ }}</p>
    <p>cell phone number:{{ }}</p>
  </div>
</template>

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

const user = ref(null);
 = await fetchUser();

async function fetchUser() {
  // utilizationsetTimeoutSimulate getting data from the server
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({
        name: "John Doe",
        phone: "13800138000",
      });
    }, 2000);
  });
}
</script>

We are in<script setup>The top level uses theawaitand then theawaitThe value you get is assigned to theuserVariables. The top level uses theawaitAfter that the subcomponent becomes an asynchronous component and waits until theawait fetchUser()After the execution, that is, after getting the data from the server, the subcomponent is considered loaded.

And since we're using theSuspenseThis is why you don't render the subcomponent until it's loaded, i.e. until you get the data from the server (the equivalent of "pausing" the rendering of the subcomponent). Instead, it renders#fallbackThe loading in the slot waits until the asynchronous subcomponent is loaded after getting the data from the server. Only then will the subcomponent be rendered for the first time, and the loading will be replaced with the content rendered by the subcomponent.

Because the first rendering of the subcomponent already got theuservalue, at which point theuserNo longer.nullso we don't have to use it at the top level of the template.v-if="user"Although there is a go read in the template

go throughParent component Suspense + child component top-level awaitAfter the modification of the rendering parent component'sSuspenseWhen it finds out that its subcomponent has an asynchronous component, it will "pause" the rendering of the subcomponent and automatically render the loading component instead.

Subcomponents in thesetuptop level usageawaitWait for the data request from the server, when the data from the server to get the sub-component at this time is considered to be loaded, then the first rendering will be carried out, and automatically replace the content of the loading for the sub-component in the rendered content.

besidesSuspenseIt also supports several asynchronous sub-components to fetch data from the server, and will automatically replace the loading with the content rendered by these asynchronous sub-components only after these sub-components have fetched the data from the server.

there's alsoSuspenseThe component is still currentlyexperimentalfeatures, production environments need to be used with caution.

Take a quick look.SuspenseHow do I "pause" rendering?

SuspenseWhen rendering a subcomponent, the render function of the asynchronous subcomponent will not be executed immediately when the subcomponent is found to be an asynchronous component. Instead, it will add a function calleddepsthat identifies the current default subcomponent as an asynchronous component.Pause RenderingAsynchronous subcomponents.

Since the asynchronous subcomponent is aPromise, so it is possible to load asynchronous subcomponents in thePromisefollowed by the addition of.then()method in the.then()method before going on to render the asynchronous subcomponent.

Currently the asynchronous subcomponent has paused rendering, and will then go and read thedepsMarker. If thedepsmark sth. astrue, indicating that the asynchronous subcomponent has paused rendering, at which point it will go ahead and move thefallbackThe loading component in the slot renders to the page.

It is triggered when the asynchronous subcomponent has finished loading thePromise(used form a nominal expression).then()method, thusContinue rendering.Asynchronous subcomponents. In the.then()method will execute the render function of the asynchronous subcomponent to generate the virtual DOM, and then generate the real DOM based on the virtual DOM.fallbackThe content in the slot is replaced with the content in the real DOM generated by the asynchronous component.

Here's the flowchart I drew (The flowchart is followed by a summary at the end of the article):
full-progress

summarize

In this post we talked about how there are scenarios that need to beAfter getting the data from the serverto render a component again, at which point we can use theParent component Suspense + child component top-level awaitof the perfect program.

When rendering the parent component'sSuspenseWhen a component finds that its children have asynchronous components, it "pauses" rendering the children and automatically renders the loading component instead.

Subcomponents in thesetuptop level usageawaitWait for the data request from the server, when the data from the server to get the sub-component at this time is considered to be loaded, then the first rendering will be carried out, and automatically replace the content of the loading for the sub-component in the rendered content.

besidesSuspenseIt also supports several asynchronous sub-components to fetch data from the server, and will automatically replace the loading with the content rendered by these asynchronous sub-components only after these sub-components have fetched the data from the server.

endSuspenseThe component is still currentlyexperimentalfeatures, production environments need to be used with caution.

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.