前言
最近想要部署自己练手的OJ项目,由于并非真的想要运营,遂懒得备案,于是利用自己最后的学生身份,在阿里云换了个300元的优惠券白嫖了一台海外(新加坡)的轻量应用服务器,结果很尴尬限制于1g的内存根本跑不起来自己的OJ项目。于是又在京东云白嫖了一个月的国内的2g内存轻量应用服务器,使用新加坡的阿里服务器反代京东的服务器以绕过备案。结果尴尬的发现,阿里云的新加坡服务器网络非常一般,加上反代海外国内来回,也没有做缓存,前端白屏时间非常差(未优化前能有十数秒)。参考网上的spa白屏时间优化的方法,减少了main.ts的import,把路由全改成懒加载,使用骨架屏(偷懒用的antd的骨架屏),在这垃圾的网络环境依旧有近3-6s的白屏时间才开始骨架屏动画。
由于项目并非真的想要运营,改成服务端渲染(ssr)的动力是一点都没有,并且也不能保证在这垃圾的网络环境ssr就能有好体验(毕竟使用ssr的话index.html也会变大)。最后虽然使用套海外cdn的方式实现加速+免备案(利用国外免费cdn(lightcdn为例)实现国内服务器免备案-CSDN博客),但还是想极限优化下这个白屏时间。
于是想到,既然index.html是最先加载完的,那么,在index.html添加一个加载动画,然后在骨架屏动画加载好的时机移除掉这个动画,那么即便在这个极端的网络情况下,也能把白屏时间减少到1s左右。
实操
使用div和css实现加载动画
<div id="index-loading"></div>
<style>
#index-loading {
--loding-width: 30px;
width: var(--loding-width);
height: var(--loding-width);
position: fixed;
top: calc(50% - var(--loding-width) / 2);
left: calc(50% - var(--loding-width) / 2);
border: 2px solid #a5dee4;
border-top-color: transparent;
border-radius: 100%;
animation: spin 0.75s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
</style>
完整index.html如下
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<div id="index-loading"></div>
<script type="module" src="/src/main.ts"></script>
</body>
<style>
#index-loading {
--loding-width: 30px;
width: var(--loding-width);
height: var(--loding-width);
position: fixed;
top: calc(50% - var(--loding-width) / 2);
left: calc(50% - var(--loding-width) / 2);
border: 2px solid #a5dee4;
border-top-color: transparent;
border-radius: 100%;
animation: spin 0.75s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
</style>
</html>
然后在适当时机移除这个div即可
document.querySelector('#index-loading')?.remove()
我是在App.vue待骨架屏动画加载完后移除的,完整代码如下
<template>
<Skeleton active v-if="loading" :paragraph="{ rows: 12 }"></Skeleton>
<RouterView />
</template>
<script setup lang="ts">
import Skeleton from 'ant-design-vue/es/skeleton/Skeleton'
import { onMounted, ref } from 'vue'
import router from './router'
const loading = ref(true)
onMounted(() => {
document.querySelector('#index-loading')?.remove()
})
router.afterEach(() => {
loading.value = false
})
</script>
<style scoped></style>
如果你需要更复杂的移除逻辑,可以考虑用MutationObserver来监视dom树的更改,具体参考https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver,将合适的逻辑加到合适的js文件里即可。
结尾
其实这个方法的适用人群预计非常小众,毕竟网络环境如此之烂最好还是考虑优化下网络环境吧,网络环境良好的情况下,将main.ts里的import优化下,路由使用懒加载,使用骨架屏已经用户体验已经足够不错了,即便还要追求首屏时间,服务端渲染才是正道。文章不过记录自己的突发奇想的解决方案罢了。