SoFunction
Updated on 2025-04-05

Summary of knowledge points for Vue first-screen performance optimization components

Vue home screen performance optimization component

Simply implement a Vue first-screen performance optimization component. Modern browsers provide many new interfaces. Without considering IE compatibility, these interfaces can greatly reduce the workload of writing code and do some performance optimization. Of course, in order to consider IE, we can also guarantee the components when encapsulating them. The first-screen performance optimization component in this article mainly uses two interfaces: IntersectionObserver and requestIdleCallback.

describe

First consider the first screen scene. When it is a first screen mainly for display, it usually loads more resources such as pictures. If we do not want to load all resources when the user opens it, but instead hope that the user will load the components when scrolling to the relevant location, you can choose the IntersectionObserver interface. Of course, you can also use the onscroll event to do a listening session, but the performance may be worse. There are also some components that we hope must be loaded, but we do not want them to load synchronously when initializing the page. In this way, we can use asynchronous methods such as Promise and setTimeout, but if we want to lower the priority of loading this component, we can consider the requestIdleCallback interface. The relevant code is in the vue-first-screen-optimization branch of /WindrunnerMax/webpack-simple-environment.

IntersectionObserver

The IntersectionObserver interface is subordinate to the Intersection Observer API and provides a method to asynchronously observe the intersection state of the target element and its ancestor element or top-level document viewport. The ancestor element and the window viewport are called root root, that is, the IntersectionObserver API can automatically observe whether the element is visible. Since the essence of visible visible visible is that the target element and the viewport produce an intersection area, this API is called a cross-observer, compatibility/?search=IntersectionObserver.

const io = new IntersectionObserver(callback, option);

// Start to observe(("example"));
// Stop observing(element);
// Turn off the observer();
  • Parameter callback, after creating a new IntersectionObserver object, the specified callback function will be executed when it listens to the visible part of the target element that has passed through one or more thresholds.
  • Parameter option, the second parameter of the IntersectionObserver constructor is a configuration object, which can set the following properties:
  • The threshold attribute determines when the callback function is triggered. It is an array, and each member has a threshold value. The default is [0], that is, the callback function is triggered when the cross-scale intersectionRatio reaches 0. The user can customize this array. For example, [0, 0.25, 0.5, 0.75, 1] ​​means that the callback function will be triggered when the target element is 0%, 25%, 50%, 75%, and 100% visible.
  • The root attribute specifies the container node where the target element is located, that is, the root element. The target element will not only scroll with the window, but also scroll in the container, such as scrolling in the iframe window. This requires setting the root attribute. Note that the container element must be the ancestor node of the target element.
  • The rootMargin attribute defines the margin of the root element, which is used to expand or reduce the size of the rootBounds rectangle, thereby affecting the size of the intersectionRect cross area. It uses CSS definition methods, such as 10px 20px 30px 40px, representing the values ​​in the four directions of top, right, bottom and left.
  • The attribute is read-only, and the specific ancestor element element of the object being listened to. If the value is not passed in or the value is null, the window of the top-level document is used by default.
  • The attribute is read-only. The rectangular offset added to the bounding box of the root bounding box when calculating the crossover can effectively narrow or expand the judgment range of the root to meet the calculation needs. The value returned by this attribute may be different from the value specified when calling the constructor. Therefore, it may need to be changed to match the internal requirements. All offsets can be expressed by pixel pixel, px, or percentage percentage, and %. The default value is 0px 0px 0px.
  • The attribute is read-only, a list containing thresholds, arranged in ascending order. Each threshold in the list is the ratio of the cross area of ​​the listening object to the boundary area. When any threshold of the listening object is crossed, a notification Notification will be generated. If the constructor does not pass in a value, the default value is 0.
  • Method() to stop listening.
  • Method() to make IntersectionObserver start listening for a target element.
  • Method() returns an array of IntersectionObserverEntry objects for all observation targets.
  • Method() to cause IntersectionObserver to stop listening for specific target elements.

In addition, when the callback function is executed, an IntersectionObserverEntry object parameter will be passed, and the information provided is as follows.

  • time: The time when visibility changes, is a high-precision time stamp in milliseconds.
  • target: The target element being observed is a DOM node object.
  • rootBounds: The information of the rectangular area of ​​the root element is the return value of the getBoundingClientRect method. If there is no root element, it scrolls directly with respect to the viewport, it returns null.
  • boundingClientRect: Information about the rectangular area of ​​the target element.
  • interferenceRect: Information about the intersection area of ​​the target element and the viewport or root element.
  • interferenceRatio: The visible proportion of the target element, that is, the proportion of the connectionRect to boundingClientRect, which is 1 when it is fully visible, and is less than or equal to 0 when it is completely invisible.
{
  time: 3893.92,
  rootBounds: ClientRect {
    bottom: 920,
    height: 1024,
    left: 0,
    right: 1024,
    top: 0,
    width: 920
  },
  boundingClientRect: ClientRect {
     // ...
  },
  intersectionRect: ClientRect {
    // ...
  },
  intersectionRatio: 0.54,
  target: element
}

requestIdleCallback

The requestIdleCallback method can accept a function that will be called during the browser's idle period, which enables developers to perform background and low-priority work on the main event loop without affecting delayed critical events, such as animations and input responses. The functions are generally executed in the order of first-in-first call. If the callback function specifies the execution timeout timeout, it is possible to disrupt the execution order in order to execute the function before timeout. Compatibility/?search=requestIdleCallback.

const handle = (callback[, options]);
  • The requestIdleCallback method returns an ID, which can be passed into the() method to end the callback.
  • The parameter callback, a reference to a function that will be called when the event loop is idle. The function will receive a parameter called IdleDeadline. This parameter can obtain the current idle time and whether the callback has been executed before the timeout time.
  • Parameter options are optional, including optional configuration parameters, with the following properties:
  • timeout: If timeout is specified and there is a positive value and the callback has not been called after timeout milliseconds, the callback task will be placed into the event loop, even if doing so may have a negative impact on performance.

accomplish

In fact, writing components is mainly to figure out how to use these two main APIs. First, focus on IntersectionObserver, because we need to use dynamic component <component />, so when we pass values ​​to it, we need to use asynchronous loading component() => import("component") form. When listening, you can consider destroying the listener after loading, or destroying it after leaving the visual area. This is mainly a strategic issue. When the page is destroyed, the Intersection Observer must be disconnected to prevent memory leakage. Using requestIdleCallback is relatively simple. You only need to execute the callback function, which is also similar to the asynchronous processing of().then.

Here is the simple implementation logic. Usually, the usage plan of observer is to use a div, etc. to occupy the placeholder first, and then monitor the container it holds in observer. When the container is in the viewport, the relevant code is in the vue-first-screen-optimization branch of /WindrunnerMax/webpack-simple-environment. Please try to use yarn for installation. You can use file to lock the version to avoid dependency problems. After running with npm run dev, you can see the order of created creation of these four lazy load components in Console. The lazy load of A's observer needs to wait until the page rendering is completed and it is judged to be in the view before loading. The first screen enables you to see directly, while the lazy load of D requires the scroll bar to slide the scroll bar to D's external container before it appears after the view appears. That is to say, as long as it does not scroll to the bottom, component D will not be loaded. In addition, attrs and listeners can be passed to lazy loaded components through component-params and component-events, similar to $attrs and $listeners. Lazy loading components have been implemented simply.

&lt;!--  --&gt;
&lt;template&gt;
    &lt;div&gt;
        &lt;section&gt;1&lt;/section&gt;
        &lt;section&gt;
            &lt;div&gt;2&lt;/div&gt;
            &lt;lazy-load
                :lazy-component="Example"
                type="observer"
                :component-params="{ content: 'Example A' }"
                :component-events="{
                    'test-event': testEvent,
                }"
            &gt;&lt;/lazy-load&gt;
        &lt;/section&gt;
        &lt;section&gt;
            &lt;div&gt;3&lt;/div&gt;
            &lt;lazy-load
                :lazy-component="Example"
                type="idle"
                :component-params="{ content: 'Example B' }"
                :component-events="{
                    'test-event': testEvent,
                }"
            &gt;&lt;/lazy-load&gt;
        &lt;/section&gt;
        &lt;section&gt;
            &lt;div&gt;4&lt;/div&gt;
            &lt;lazy-load
                :lazy-component="Example"
                type="lazy"
                :component-params="{ content: 'Example C' }"
                :component-events="{
                    'test-event': testEvent,
                }"
            &gt;&lt;/lazy-load&gt;
        &lt;/section&gt;
        &lt;section&gt;
            &lt;div&gt;5&lt;/div&gt;
            &lt;lazy-load
                :lazy-component="Example"
                type="observer"
                :component-params="{ content: 'Example D' }"
                :component-events="{
                    'test-event': testEvent,
                }"
            &gt;&lt;/lazy-load&gt;
        &lt;/section&gt;
    &lt;/div&gt;
&lt;/template&gt;

&lt;script lang="ts"&gt;
import { Component, Vue } from "vue-property-decorator";
import LazyLoad from "./components/lazy-load/";
@Component({
    components: { LazyLoad },
})
export default class App extends Vue {
    protected Example = () =&gt; import("./components/example/");

    protected testEvent(content: string) {
        (content);
    }
}
&lt;/script&gt;

&lt;style lang="scss"&gt;
@import "./common/";
body {
    padding: 0;
    margin: 0;
}
section {
    margin: 20px 0;
    color: #fff;
    height: 500px;
    background: $color-blue;
}
&lt;/style&gt;
Copy
&lt;!--  --&gt;
&lt;template&gt;
    &lt;div&gt;
        &lt;component
            :is="renderComponent"
            v-bind="componentParams"
            v-on="componentEvents"
        &gt;&lt;/component&gt;
    &lt;/div&gt;
&lt;/template&gt;

&lt;script lang="ts"&gt;
import { Component, Prop, Vue } from "vue-property-decorator";
@Component
export default class LazyLoad extends Vue {
    @Prop({ type: Function, required: true })
    lazyComponent!: () =&gt; Vue;
    @Prop({ type: String, required: true })
    type!: "observer" | "idle" | "lazy";
    @Prop({ type: Object, default: () =&gt; ({}) })
    componentParams!: Record&lt;string, unknown&gt;;
    @Prop({ type: Object, default: () =&gt; ({}) })
    componentEvents!: Record&lt;string, unknown&gt;;

    protected observer: IntersectionObserver | null = null;
    protected renderComponent: (() =&gt; Vue) | null = null;

    protected mounted() {
        ();
    }

    private init() {
        if ( === "observer") {
            // Existence ```            if () {
                 = new IntersectionObserver(entries =&gt; {
                    (item =&gt; {
                        // `intersectionRatio` is the visible proportion of the target element, and greater than `0` means visible                        // There are also implementation strategies here, such as not removing `observe` after loading and destroying it when it is invisible, etc.                        if ( &gt; 0) {
                            ();
                            // Unload it after loading is completed `observe`                            ?.unobserve();
                        }
                    });
                });
                (this.$ || this.$el);
            } else {
                // Direct loading                ();
            }
        } else if ( === "idle") {
            // There is `requestIdleCallback`            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            if () {
                requestIdleCallback(, { timeout: 3 });
            } else {
                // Direct loading                ();
            }
        } else if ( === "lazy") {
            // `Promise` exists`            if () {
                ().then();
            } else {
                // Downgrade to use `setTimeout`                setTimeout();
            }
        } else {
            throw new Error(`type: "observer" | "idle" | "lazy"`);
        }
    }

    private loadComponent() {
         = ;
        this.$emit("loaded");
    }

    protected destroyed() {
         &amp;&amp; ();
    }
}
&lt;/script&gt;

One question every day

/WindrunnerMax/EveryDay

refer to

/blog/2016/11/intersectionobserver_api.html

/zh-CN/docs/Web/API/IntersectionObserver

/zh-CN/docs/Web/API/Window/requestIdleCallback

This is the end of this article about the summary of the knowledge points of Vue's first-screen performance optimization component. For more related contents of Vue's first-screen performance optimization component, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!