优化
刀刀
3/20/2025
0 字
0 分钟
图片处理
webp
在线网址把图片转换为 webp 格式,可以显著减少图片的大小,提升加载速度。
懒加载与占位图
在进入文章列表和效果列表时,由于图片资源较多,需要一定的时间进行加载,图片未加载时样式相对丑陋。为了提升使用体验,在图片未加载时使用占位图来代替图片,待图片加载完毕后再替换原图。
除此之外还运用到了懒加载,还未出现在视口内的图片会先不加载,出现在视口内之后才加载。
为了方便使用管理和后续维护,封装一个 my-image
组件,将图片的懒加载和占位图功能封装在其中。在 my-image
组件中,通过封装 v-lazy
自定义指令实现懒加载,通过 :data-src
属性绑定图片的地址,通过 src
属性绑定占位图的地址。
自定义指令 自定义指令主要通过
IntersectionObserver
API 判断当前元素是否出现在视口内,出现在视口内则通过getAttribute
获取自定义元素的data-src
属性,将其赋值给src
属性。然后取消监听。jsimport imgErr from '@/assets/img/base/img_err.png'; // IntersectionObserver API const ob = new IntersectionObserver((entries) => { for (const entry of entries) { if (entry.isIntersecting) { // 出现在视口中 const img = entry.target; const src = img.getAttribute('data-src'); // 获取图片的src属性 img.src = src; // 设置图片的src属性 img.onload = () => { img.setAttribute('class', 'fade-in'); } img.onerror = () => { img.src = imgErr; // 设置图片的src属性为错误图片 } ob.unobserve(entry.target); } } }) export default { mounted(el, binding) { // 判断当前元素是否在视口之上或者视口内,在的话不需要播放动画 // if (!isBelowViewPort(el)) return; ob.observe(el); // 观察元素是否进入视口 }, unmounted(el) { ob.unobserve(el); // 元素卸载后断开观察 } }
还有一下小细节比如图片加载失败后用失败占位图来代替,图片加载成功后添加
fade-in
动画等,这里不做过多赘述。组件
vue<script setup> defineProps({ src: { type: String, required: true, }, alt: { type: String, default: '占位图片', }, }) </script> <template> <img src="@/assets/img/base/img_load.png" v-lazy :data-src="src" :alt="alt" /> </template> <style lang="less" scoped> img { width: 100%; height: 100%; &.fade-in { animation: fadeIn .6s ease-in; } } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } </style>
预加载
部分图片如占位图、nav
标签背景图等,在网络不是太好时没加载出来。为了提升使用体验,提前将图片资源加载到浏览器缓存中。
预加载通过 <line href="图片资源路径" rel="preload" as="image" />
标签实现,为了方便后续维护,新建一个 js 文件专门处理预加载。
- 引入所有需要预加载的图片资源
- 遍历,创建
link
标签,为href
属性赋值,添加到head
中 - 在
main.js
引入该文件
import img_err from '../assets/img/base/img_err.png';
import img_load from '../assets/img/base/img_load.png';
import left_active from '../assets/img/base/left_active.png';
import left_inactive from '../assets/img/base/left_inactive.png';
import right_inactive from '../assets/img/base/right_inactive.png';
import right_active from '../assets/img/base/right_active.png';
const data = {
img_err,
img_load,
left_active,
left_inactive,
right_inactive,
right_active,
};
const createPreloadLink = () => {
Object.keys(data).forEach((key) => {
const path = data[key];
const link = document.createElement('link');
link.rel = 'preload';
link.as = 'image';
link.href = path;
document.head.appendChild(link);
})
}
createPreloadLink();
第三方库
该项目引入了百度地图和 md5
两个第三方库,但是在加载的时候,由于这两个库的包体积较大,导致首屏加载时间过长。为了优化首屏加载速度,修改为 pnpm i
和按需加载的方式。
/**
* 预加载百度地图API
*
* @returns 返回一个Promise对象,加载成功时resolve,加载失败时reject
*/
export const preloadBMap = () => {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = 'https://api.map.baidu.com/api?v=3.0&s=1&ak=42fr9YjV9zQPGeuHeOD5DpXGY8InSmCI&callback=baiduMapInit';
script.type = 'text/javascript';
script.async = true;
script.onload = () => resolve();
script.onerror = () => reject();
document.head.appendChild(script);
});
}
在需要的组件中引入该函数,并在 onMounted
钩子中调用。