解决vite项目首次打开页面卡顿的问题
问题描述
在vite项目中我们可能会遇到这样一种情况。
在我们本地开发,第一次进入页面的时候,页面会卡顿很长时间。越是复杂的卡顿时间越久。
要是我们一天只专注于一两个页面 那这个就不是问题。
但有的时候我们要开发一个流程性的东西,要进入各种各样的页面查看。这样就很痛苦了。
问题原因
为什么会出现这种情况呢?因为路由的懒加载与vite的编译机制。
路由的懒加载:没有进入过的页面不加载
vite的编译机制:没有加载的不编译。
这样就会出现 我们在进入一个新页面的时候他才会编译。我们感觉卡顿的过程就是他编译的过程。
解决思路
问题找到了,那么解决起来就简单了。我们本地开发的时候,取消路由的懒加载就可以了。
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类型监听所有的网络请求,代码示例如下
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`] })
监听到网络请求后,我们怎么判断是否空闲呢?也很简单,只要一秒钟以内没有新的网络请求出现我们就认为当前网络是空闲的。这不就防抖函数嘛。
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秒钟后执行)还要根据你的实际项目进行修改!
可优化项
以上方法确实是按照我们的预期实现了,但是还有一些小小的问题。例如:
- 我们在加载组件的时候如果恰好是当前打开的页面,是不会重新触发网络请求的。因此可能会断掉componentsToLoad数组的删除,加载组件,触发网络请求这个流程。不过问题不大,你在当前页面如果有操作重新触发网络请求了,这个流程还会继续走下去,直到componentsToLoad数组为空。
- 每次刷新页面componentsToLoad数组都是会重新获取到值的,也就是我们走过的流程会重新走。不过问题不大,第二次走都是走缓存了,执行速度很快,而且也是本地开发那点性能损坏可以忽略不计。
这些问题影响都不是很大,所以我就没继续做优化。有兴趣的小伙伴可以继续研究下去。
来源:juejin.cn/post/7280745727160811579