解决vite项目首次打开页面卡顿的问题

问题描述

在vite项目中我们可能会遇到这样一种情况。

在我们本地开发,第一次进入页面的时候,页面会卡顿很长时间。越是复杂的卡顿时间越久。

要是我们一天只专注于一两个页面 那这个就不是问题。

但有的时候我们要开发一个流程性的东西,要进入各种各样的页面查看。这样就很痛苦了。

问题原因

为什么会出现这种情况呢?因为路由的懒加载与vite的编译机制。

路由的懒加载:没有进入过的页面不加载

vite的编译机制:没有加载的不编译。

这样就会出现 我们在进入一个新页面的时候他才会编译。我们感觉卡顿的过程就是他编译的过程。

解决思路

问题找到了,那么解决起来就简单了。我们本地开发的时候,取消路由的懒加载就可以了。

js 复制代码
const routes = [
  {
    path: `/home`,
    name: `Home`,
    component: () => import(`@/views/home/HomePage.vue`),
    meta: { title: `首页` },
  },
  {
    path: `/test1`,
    name: `test1`,
    component: () => import(`@/views/demo/Test1.vue`),
    meta: { title: `测试1` },
  },
  {
    path: `/test2`,
    name: `test2`,
    component: () => import(`@/views/demo/Test2.vue`),
    meta: { title: `测试2` },
  }
]

if (import.meta.env.MODE === `development`) {
  routes.forEach(item => item.component())
}

示例代码如上。上述的问题是解决了,但是又产生了新的问题。项目太大的时候启动会非常慢。

于是我想了一个折中的方案。初始打开项目的时候路由还是懒加载的,然后我在浏览器网络空闲的时候去加载资源。这样你首次进系统打开的第一个页面可能还是需要等待,但是之后的所有页面就不需要等待了。

那么问题又来了?怎么监听浏览器的网络空闲呢?这就要用的浏览器的一个api PerformanceObserver 。这个api可能很多小伙伴都不知道,它主要是帮助你监控和观察与性能相关的各种指标。想要详细了解的可以点击这里查看

我们今天用的就是resource类型监听所有的网络请求,代码示例如下

js 复制代码
 const observer: PerformanceObserver = new PerformanceObserver((list: PerformanceObserverEntryList) => {
    const entries: PerformanceEntryList = list.getEntries()
    for (const entry of entries) {
      if (entry.entryType === `resource`) {
        //网络请求结束
      }
    }
  })
  observer.observe({ entryTypes: [`resource`] })

监听到网络请求后,我们怎么判断是否空闲呢?也很简单,只要一秒钟以内没有新的网络请求出现我们就认为当前网络是空闲的。这不就防抖函数嘛。

js 复制代码
const routes = [
  {
    path: `/home`,
    name: `Home`,
    component: () => import(`@/views/home/HomePage.vue`),
    meta: { title: `首页` },
  },
  {
    path: `/test1`,
    name: `test1`,
    component: () => import(`@/views/demo/Test1.vue`),
    meta: { title: `测试1` },
  },
  {
    path: `/test2`,
    name: `test2`,
    component: () => import(`@/views/demo/Test2.vue`),
    meta: { title: `测试2` },
  }
]

if (import.meta.env.MODE === `development`) {
      const componentsToLoad = routes.map(item => item.component)
      const loadComponentsWhenNetworkIdle = debounce(
        () => {
          if (componentsToLoad.length > 0) {
            const componentLoader = componentsToLoad.pop()
            componentLoader && componentLoader()
            // eslint-disable-next-line
            console.log(`剩余${componentsToLoad.length}个路由未加载`, componentsToLoad)
          }
        },
        1000,
        false
      )

      const observer: PerformanceObserver = new PerformanceObserver((list: PerformanceObserverEntryList) => {
        const entries: PerformanceEntryList = list.getEntries()
        for (const entry of entries) {
          if (entry.entryType === `resource`) {
            loadComponentsWhenNetworkIdle()
          }
        }
      })
      observer.observe({ entryTypes: [`resource`] })
}

完整的代码如上。当我们判断出网络空闲后,就从componentsToLoad数组中删除一个组件,并加载删除的这个组件,然后就会重新触发网络请求。一直重复这个流程,直到componentsToLoad数组为空。

这只是个示例的代码,获取componentsToLoad变量防抖函数的配置(初始化不执行,无操作后1秒钟后执行)还要根据你的实际项目进行修改!

可优化项

以上方法确实是按照我们的预期实现了,但是还有一些小小的问题。例如:

  1. 我们在加载组件的时候如果恰好是当前打开的页面,是不会重新触发网络请求的。因此可能会断掉componentsToLoad数组的删除,加载组件,触发网络请求这个流程。不过问题不大,你在当前页面如果有操作重新触发网络请求了,这个流程还会继续走下去,直到componentsToLoad数组为空。
  2. 每次刷新页面componentsToLoad数组都是会重新获取到值的,也就是我们走过的流程会重新走。不过问题不大,第二次走都是走缓存了,执行速度很快,而且也是本地开发那点性能损坏可以忽略不计。

这些问题影响都不是很大,所以我就没继续做优化。有兴趣的小伙伴可以继续研究下去。

相关推荐
Senar24 分钟前
Web端选择本地文件的几种方式
前端·javascript·html
烛阴41 分钟前
UV Coordinates & Uniforms -- OpenGL UV坐标和Uniform变量
前端·webgl
姑苏洛言1 小时前
扫码小程序实现仓库进销存管理中遇到的问题 setStorageSync 存储大小限制错误解决方案
前端·后端
烛阴1 小时前
JavaScript 的 8 大“阴间陷阱”,你绝对踩过!99% 程序员崩溃瞬间
前端·javascript·面试
lh_12541 小时前
ECharts 地图开发入门
前端·javascript·echarts
jjw_zyfx1 小时前
成熟的前端vue vite websocket,Django后端实现方案包含主动断开websocket连接的实现
前端·vue.js·websocket
Mikey_n2 小时前
前台调用接口的方式及速率对比
前端
周之鸥2 小时前
使用 Electron 打包可执行文件和资源:完整实战教程
前端·javascript·electron
我爱吃朱肉2 小时前
HTMLCSS模板实现水滴动画效果
前端·css·css3
机器视觉知识推荐、就业指导2 小时前
开源QML控件:进度条滑动控件(含源码下载链接)
前端·qt·开源·qml