文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Vue首屏性能优化组件知识点总结

2024-04-02 19:55

关注

Vue首屏性能优化组件

简单实现一个Vue首屏性能优化组件,现代化浏览器提供了很多新接口,在不考虑IE兼容性的情况下,这些接口可以很大程度上减少编写代码的工作量以及做一些性能优化方面的事情,当然为了考虑IE我们也可以在封装组件的时候为其兜底,本文的首屏性能优化组件主要是使用IntersectionObserver以及requestIdleCallback两个接口。

描述

先考虑首屏场景,当做一个主要为展示用的首屏时,通常会加载较多的资源例如图片等,如果我们不想在用户打开时就加载所有资源,而是希望用户滚动到相关位置时再加载组件,此时就可以选择IntersectionObserver这个接口,当然也可以使用onscroll事件去做一个监听,只不过这样性能可能比较差一些。还有一些组件,我们希望他必须要加载,但是又不希望他在初始化页面时同步加载,这样我们可以使用异步的方式比如Promise和setTimeout等,但是如果想再降低这个组件加载的优先级,我们就可以考虑requestIdleCallback这个接口,相关代码在https://github.com/WindrunnerMax/webpack-simple-environment的vue--first-screen-optimization分支。

IntersectionObserver

IntersectionObserver接口,从属于Intersection Observer API,提供了一种异步观察目标元素与其祖先元素或顶级文档视窗viewport交叉状态的方法,祖先元素与视窗viewport被称为根root,也就是说IntersectionObserver API,可以自动观察元素是否可见,由于可见visible的本质是,目标元素与视口产生一个交叉区,所以这个API叫做交叉观察器,兼容性https://caniuse.com/?search=IntersectionObserver。


const io = new IntersectionObserver(callback, option);

// 开始观察
io.observe(document.getElementById("example"));
// 停止观察
io.unobserve(element);
// 关闭观察器
io.disconnect();

此外当执行callback函数时,会传递一个IntersectionObserverEntry对象参数,其提供的信息如下。


{
  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

requestIdleCallback方法能够接受一个函数,这个函数将在浏览器空闲时期被调用,这使开发者能够在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件,如动画和输入响应,函数一般会按先进先调用的顺序执行,如果回调函数指定了执行超时时间timeout,则有可能为了在超时前执行函数而打乱执行顺序,兼容性https://caniuse.com/?search=requestIdleCallback。


const handle = window.requestIdleCallback(callback[, options]);

实现

实际上编写组件主要是搞清楚如何使用这两个主要的API就好,首先关注IntersectionObserver,因为考虑需要使用动态组件<component />,那么我们向其传值的时候就需要使用异步加载组件() => import("component")的形式。监听的时候,可以考虑加载完成之后即销毁监听器,或者离开视觉区域后就将其销毁等,这方面主要是策略问题。在页面销毁的时候就必须将Intersection Observer进行disconnect,防止内存泄漏。使用requestIdleCallback就比较简单了,只需要将回调函数执行即可,同样也类似于Promise.resolve().then这种异步处理的情况。

这里是简单的实现逻辑,通常observer的使用方案是先使用一个div等先进行占位,然后在observer监控其占位的容器,当容器在视区时加载相关的组件,相关的代码在https://github.com/WindrunnerMax/webpack-simple-environment的vue--first-screen-optimization分支,请尽量使用yarn进行安装,可以使用yarn.lock文件锁住版本,避免依赖问题。使用npm run dev运行之后可以在Console中看到这四个懒加载组件created创建的顺序,其中A的observer懒加载是需要等其加载页面渲染完成之后,判断在可视区,才进行加载,首屏使能够直接看到的,而D的懒加载则是需要将滚动条滑动到D的外部容器出现在视图之后才会出现,也就是说只要不滚动到底部是不会加载D组件的,另外还可以通过component-params和component-events将attrs和listeners传递到懒加载的组件,类似于$attrs和$listeners,至此懒加载组件已简单实现。


<!-- App.vue -->
<template>
    <div>
        <section>1</section>
        <section>
            <div>2</div>
            <lazy-load
                :lazy-component="Example"
                type="observer"
                :component-params="{ content: 'Example A' }"
                :component-events="{
                    'test-event': testEvent,
                }"
            ></lazy-load>
        </section>
        <section>
            <div>3</div>
            <lazy-load
                :lazy-component="Example"
                type="idle"
                :component-params="{ content: 'Example B' }"
                :component-events="{
                    'test-event': testEvent,
                }"
            ></lazy-load>
        </section>
        <section>
            <div>4</div>
            <lazy-load
                :lazy-component="Example"
                type="lazy"
                :component-params="{ content: 'Example C' }"
                :component-events="{
                    'test-event': testEvent,
                }"
            ></lazy-load>
        </section>
        <section>
            <div>5</div>
            <lazy-load
                :lazy-component="Example"
                type="observer"
                :component-params="{ content: 'Example D' }"
                :component-events="{
                    'test-event': testEvent,
                }"
            ></lazy-load>
        </section>
    </div>
</template>

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

    protected testEvent(content: string) {
        console.log(content);
    }
}
</script>

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

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

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

    protected mounted() {
        this.init();
    }

    private init() {
        if (this.type === "observer") {
            // 存在`window.IntersectionObserver`
            if (window.IntersectionObserver) {
                this.observer = new IntersectionObserver(entries => {
                    entries.forEach(item => {
                        // `intersectionRatio`为目标元素的可见比例,大于`0`代表可见
                        // 在这里也有实现策略问题 例如加载后不解除`observe`而在不可见时销毁等
                        if (item.intersectionRatio > 0) {
                            this.loadComponent();
                            // 加载完成后将其解除`observe`
                            this.observer?.unobserve(item.target);
                        }
                    });
                });
                this.observer.observe(this.$el.parentElement || this.$el);
            } else {
                // 直接加载
                this.loadComponent();
            }
        } else if (this.type === "idle") {
            // 存在`requestIdleCallback`
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            if (window.requestIdleCallback) {
                requestIdleCallback(this.loadComponent, { timeout: 3 });
            } else {
                // 直接加载
                this.loadComponent();
            }
        } else if (this.type === "lazy") {
            // 存在`Promise`
            if (window.Promise) {
                Promise.resolve().then(this.loadComponent);
            } else {
                // 降级使用`setTimeout`
                setTimeout(this.loadComponent);
            }
        } else {
            throw new Error(`type: "observer" | "idle" | "lazy"`);
        }
    }

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

    protected destroyed() {
        this.observer && this.observer.disconnect();
    }
}
</script>

每日一题

https://github.com/WindrunnerMax/EveryDay

参考

https://www.ruanyifeng.com/blog/2016/11/intersectionobserver_api.html

https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserver

https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestIdleCallback

到此这篇关于Vue首屏性能优化组件知识点总结的文章就介绍到这了,更多相关Vue首屏性能优化组件内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-前端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯