Vue项目首屏性能优化组件实战指南

目录
  • Vue首屏性能优化组件
  • 描述
    • IntersectionObserver
    • requestIdleCallback
  • 实现
  • 总结

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,创建一个新的IntersectionObserver对象后,当其监听到目标元素的可见部分穿过了一个或多个阈thresholds时,会执行指定的回调函数。
  • 参数option,IntersectionObserver构造函数的第二个参数是一个配置对象,其可以设置以下属性:
    • threshold属性决定了什么时候触发回调函数,它是一个数组,每个成员都是一个门槛值,默认为[0],即交叉比例intersectionRatio达到0时触发回调函数,用户可以自定义这个数组,比如[0, 0.25, 0.5, 0.75, 1]就表示当目标元素0%、25%、50%、75%、100%可见时,会触发回调函数。
    • root属性指定了目标元素所在的容器节点即根元素,目标元素不仅会随着窗口滚动,还会在容器里面滚动,比如在iframe窗口里滚动,这样就需要设置root属性,注意,容器元素必须是目标元素的祖先节点。
    • rootMargin属性定义根元素的margin,用来扩展或缩小rootBounds这个矩形的大小,从而影响intersectionRect交叉区域的大小,它使用CSS的定义方法,比如10px 20px 30px 40px,表示top、right、bottom和left四个方向的值。
  • 属性IntersectionObserver.root只读,所监听对象的具体祖先元素element,如果未传入值或值为null,则默认使用顶级文档的视窗。
  • 属性IntersectionObserver.rootMargin只读,计算交叉时添加到根root边界盒bounding box的矩形偏移量,可以有效的缩小或扩大根的判定范围从而满足计算需要,此属性返回的值可能与调用构造函数时指定的值不同,因此可能需要更改该值,以匹配内部要求,所有的偏移量均可用像素pixel、px或百分比percentage、%来表达,默认值为0px 0px 0px 0px。
  • 属性IntersectionObserver.thresholds只读,一个包含阈值的列表,按升序排列,列表中的每个阈值都是监听对象的交叉区域与边界区域的比率,当监听对象的任何阈值被越过时,都会生成一个通知Notification,如果构造器未传入值,则默认值为0。
  • 方法IntersectionObserver.disconnect(),使IntersectionObserver对象停止监听工作。
  • 方法IntersectionObserver.observe(),使IntersectionObserver开始监听一个目标元素。
  • 方法IntersectionObserver.takeRecords(),返回所有观察目标的IntersectionObserverEntry对象数组。
  • 方法IntersectionObserver.unobserve(),使IntersectionObserver停止监听特定目标元素。

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

  • time:可见性发生变化的时间,是一个高精度时间戳,单位为毫秒。
  • target:被观察的目标元素,是一个DOM节点对象。
  • rootBounds:根元素的矩形区域的信息,是getBoundingClientRect方法的返回值,如果没有根元素即直接相对于视口滚动,则返回null。
  • boundingClientRect:目标元素的矩形区域的信息。
  • intersectionRect:目标元素与视口或根元素的交叉区域的信息。
  • intersectionRatio:目标元素的可见比例,即intersectionRect占boundingClientRect的比例,完全可见时为1,完全不可见时小于等于0。
{
  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]);
  • requestIdleCallback方法返回一个ID,可以把它传入window.cancelIdleCallback()方法来结束回调。
  • 参数callback,一个在事件循环空闲时即将被调用的函数的引用,函数会接收到一个名为IdleDeadline的参数,这个参数可以获取当前空闲时间以及回调是否在超时时间前已经执行的状态。
  • 参数options可选,包括可选的配置参数,具有如下属性:
    • timeout: 如果指定了timeout,并且有一个正值,而回调在timeout毫秒过后还没有被调用,那么回调任务将放入事件循环中排队,即使这样做有可能对性能产生负面影响。

实现

实际上编写组件主要是搞清楚如何使用这两个主要的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>

每日一题

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首屏性能优化组件内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Vue.js九个性能优化技巧(值得收藏)

    目录 Functional components Child component splitting Local variables Reuse DOM with v-show KeepAlive Deferred features Time slicing Non-reactive data Virtual scrolling 总结 参考资料 这篇文章主要参考了 Vue.js 核心成员Guillaume Chau在 19 年美国的 Vue conf 分享的主题:9 Performance se

  • 浅谈Vue 初始化性能优化

    前言 一般来说,你不需要太关心vue的运行时性能,它在运行时非常快,但付出的代价是初始化时相对较慢.在最近开发的一个Hybrid APP里,Android Webview初始化一个较重的vue页面竟然用了1200ms ~ 1400ms,这让我开始重视vue的初始化性能,并最终优化到200 ~ 300ms,这篇文章分享我的优化思路. 性能瓶颈在哪里? 先看一下常见的vue写法:在html里放一个app组件,app组件里又引用了其他的子组件,形成一棵以app为根节点的组件树. <body> <

  • Vue性能优化的方法

    今天来谈一谈Vue中一些性能优化的问题,仅仅是个人使用中的一些小心得,来,今天我一句废话不多说,直接上内容好吧 1.v-if和v-show的使用, 我们都知道这两个都可以控制显隐,那我们用哪个呢,个人觉得要从两个方面入手来确定使用哪个, 1.权限的问题,只要涉及到权限相关的展示用v-if比较好 2.切换地频率,如果频繁的切换我们用v-show,不频繁的切换用v-if 其实两者各有优缺,就看你是怎么选择了,用v-if能减少页面中的DOM总数,加快渲染的速度,而且我们要清楚一个事情 v-if是'真正

  • Vue2.x 项目性能优化之代码优化的实现

    众所周知,Vue项目采用了数据双向绑定和虚拟DOM基础,在数据驱动代替DOM频繁渲染已经算是非常高效了,对开发者而言已经非常优化了,那为什么还会有Vue性能优化这一说呢? 因为目前Vue 2.x使用了webpack等第三方打包构建工具,并且支持其他第三方的插件,我们在项目中使用这些工具时可能不同的操作在运行或打包效率上会有不同的效果,下面就来详细说明优化的方向. 1 v-if 和 v-show 的使用 v-if 为false的时候不会渲染DOM到视图,为true的时候才会渲染到视图: v-sho

  • Vue项目首屏性能优化组件实战指南

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

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

    Vue首屏性能优化组件 简单实现一个Vue首屏性能优化组件,现代化浏览器提供了很多新接口,在不考虑IE兼容性的情况下,这些接口可以很大程度上减少编写代码的工作量以及做一些性能优化方面的事情,当然为了考虑IE我们也可以在封装组件的时候为其兜底,本文的首屏性能优化组件主要是使用IntersectionObserver以及requestIdleCallback两个接口. 描述 先考虑首屏场景,当做一个主要为展示用的首屏时,通常会加载较多的资源例如图片等,如果我们不想在用户打开时就加载所有资源,而是希望

  • vue项目首屏加载时间优化实战

    问题 单页面应用的一个问题就是首页加载东西过多,加载时间过长.特别在移动端,单页面应用的首屏加载优化更是绕不开的话题.下面我会写出我在项目中做的一些优化,希望大家能够相互讨论,共同进步. 我的项目 vue-cli3构建的,vue+vue-router+vuex,UI框架选用 element-ui,ajax方案选用 axios,服务器使用Nginx .用到的这些技术都是现在用的比较广泛的,看到这篇文章,我估计你和我用的技术应该差不多. 第一步:webpack-bundle-analyzer 分析

  • vue项目首屏打开速度慢的解决方法

    最近接手了一个后台管理系统,技术栈主要是vue全家桶+elementui,老大打开测试环境页面的时候,说看到首页需要6秒钟,那如何进行优化呢? 首先我们需要安装webpack-bundle-analyzer // webpack.prod.conf.js if (config.build.bundleAnalyzerReport) { const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlu

  • Vue SPA 首屏优化方案

    前言 常规 vue 项目打包后访问,返回一个只包含 div 的 html,其他内容块都是通过 js 动态生成的. 存在两个比较大的问题: 不利于 seo 首屏加载页慢,用户体验不好 本文是自己根据项目经验总结的方案,如有不足,欢迎指出~ 优化 SSR SSR(Server-Side Rendering) 即服务端渲染,把 vue 组件在服务器端渲染为组装好的 HTML 字符串,然后将它们直接发送到浏览器,最后需要将这些静态标记混合在客户端上完全可交互的应用程序. 使用 ssr 重新部署构建项目后

  • 浅谈Vue SPA 首屏加载优化实践

    写在前面 本文记录笔者在Vue SPA项目首屏加载优化过程中遇到的一些坑及优化方案! 我们以 vue-cli工具为例,使用 vue-router搭建SPA应用,UI框架选用 element-ui, ajax方案选用 axios, 并引入vuex,使用 vuex-router-sync将 router 同步到 store ,服务器使用本地Nginx服务. 构建项目 vue-init webpack vue-spa-starter-kit cd vue-spa-starter-kit npm ins

  • Vue SPA首屏加载缓慢问题解决方案

    目录 首屏加载 关于计算首屏时间 加载慢的原因 解决方案 静态资源本地缓存 UI框架按需加载 组件重复打包 图片资源的压缩 开启GZip压缩 使用SSR 首屏加载 首屏时间(First Contentful Paint),指的是浏览器从响应用户输入网址地址,到首屏内容渲染完成的时间,此时整个网页不一定要全部渲染完成,但需要展示当前视窗需要的内容 首屏加载可以说是用户体验中最重要的环节 关于计算首屏时间 利用performance.timing提供的数据: 通过DOMContentLoad或者pe

  • vue短信验证性能优化如何写入localstorage中

    平时我们在项目中进行注册等的时候,会经常用到短信验证的功能,但是现在现在很多短信验证都是存在下面几个问题,例如短信验证时间为60s的时候, 1. 当点击完按钮时,倒计时还没到60s过完时,刷新浏览器,验证码按钮又可以重新点击 2.当点击按钮倒计时开始,例如在50s的时候我关闭了浏览器,过了5s后,我在打开,此时时间倒计时的时间应该是45s左右,但是当重新打开浏览器的时候,按钮又可以重新点击了 为了解决上面的两个问题,就需要把时间都写到localstorage里面去,当打开页面的时候,就去loca

  • 解决VUE项目使用Element-ui 下拉组件的验证失效问题

    问题描述: 在使用Element-ui组件做项目开发时候有可能会使用下拉框组件,如果下拉框组件的option选项是使用v-if指令遍历的, 这样也没有问题,但是如果加上multiple属性,也就是可以多选 就会出现下拉框验证时失效问题. 问题现象: 如图所示,明明已经选择了选项,可是验证还是为空,因此不能提交请求 解决思路: 如果说找遍组件里的方法都无法获得我们适合使用的,那就要动用原生基本功了 console.log一下v-model在此组件上绑定的属性,你发现你绑定的字符串(一般情况下都是S

随机推荐