前端 性能优化 (图片与样式篇)

文章目录

前端能够做哪些图片优化?

1、减小图片体积

压缩图片可以减小文件大小,提升页面加载速度。可以使用工具如 TinyPNGImageOptim 来优化图片。

2、图片缓存

浏览器缓存是最基础的前端缓存方式。通过设置HTTP响应头,指定缓存策略,可以控制浏览器对图片资源的缓存行为。

Cache-Control: 设置图片的缓存时长。例如,Cache-Control: max-age=31536000表示缓存一年。

Expires: 指定缓存过期日期,配合Cache-Control使用。

ETag 和 Last-Modified: 标记资源的版本号或最后修改时间,当浏览器再次请求该资源时,通过对比判断是否需要重新下载。

这些头信息可以在服务器端配置,常用于CDN或静态资源服务器。

服务工作线程 (Service Workers)缓存

Service Workers是一种更高级的缓存方式,通过注册Service Worker脚本,可以拦截图片请求并从缓存中返回内容。可以在应用初始化时加载图片资源并存储在Service Worker的缓存中,后续请求可以直接读取缓存内容。

typescript 复制代码
// 安装阶段(Install Event),当 Service Worker 安装时触发
self.addEventListener('install', event => {
    // 使用 event.waitUntil 确保安装过程完成后才继续执行后续的步骤
    event.waitUntil(
        // 打开一个名为 'image-cache' 的缓存存储
        caches.open('image-cache').then(cache => {
            // 将图片资源添加到缓存中
            return cache.addAll([
                '/images/photo1.jpg', // 图片1
                '/images/photo2.jpg', // 图片2
                // 更多图片资源可以添加在这里...
            ]);
        })
    );
});

// 激活阶段,拦截网络请求并返回缓存数据(Fetch Event)
self.addEventListener('fetch', event => {
    // 判断请求是否是图片资源
    if (event.request.destination === 'image') {
        // event.respondWith 接受一个 Promise,返回响应数据
        event.respondWith(
            // 尝试从缓存中匹配请求
            caches.match(event.request).then(response => {
                // 如果缓存中存在图片,直接返回缓存的响应
                return response || 
                    // 如果缓存中没有对应的图片,则执行网络请求
                    fetch(event.request).then(fetchResponse => {
                        // 一旦网络请求返回图片数据,就将其缓存
                        return caches.open('image-cache').then(cache => {
                            // 将网络获取的图片存入缓存
                            cache.put(event.request, fetchResponse.clone());
                            // 返回从网络获取的图片响应
                            return fetchResponse;
                        });
                    });
            })
        );
    }
});

IndexDB缓存图片

IndexedDB一个适合存储较大数据(如图片二进制数据)的浏览器数据库。要存储图片,我们通常将图片文件转换为 Blob(二进制大对象),并存入 IndexedDB。这样,在后续需要加载缓存图片时,可以从 IndexedDB 中检索 Blob,然后将其转化为 URL 以加载到页面中。

初始化 IndexedDB 数据库和对象存储

typescript 复制代码
// 初始化 IndexedDB
function openDatabase() {
    return new Promise((resolve, reject) => {
        const request = indexedDB.open('imageCacheDB', 1);

        // 创建或更新数据库时触发
        request.onupgradeneeded = function(event) {
            const db = event.target.result;
            if (!db.objectStoreNames.contains('images')) {
                db.createObjectStore('images'); // 创建名为 "images" 的对象存储
            }
        };

        request.onsuccess = function(event) {
            resolve(event.target.result); // 成功打开数据库
        };

        request.onerror = function(event) {
            reject(event.target.error); // 打开数据库失败
        };
    });
}

将图片存储到 IndexedDB 中

typescript 复制代码
// 将图片 Blob 存入 IndexedDB
function cacheImage(url, blob) {
    openDatabase().then(db => {
        const transaction = db.transaction('images', 'readwrite');
        const store = transaction.objectStore('images');
        store.put(blob, url); // 使用图片 URL 作为键
        transaction.oncomplete = () => {
            console.log('Image cached in IndexedDB');
        };
    }).catch(error => {
        console.error('Failed to cache image:', error);
    });
}

// 获取图片并缓存
function fetchAndCacheImage(url) {
    fetch(url)
        .then(response => response.blob())
        .then(blob => {
            cacheImage(url, blob); // 缓存图片
        })
        .catch(error => {
            console.error('Failed to fetch image:', error);
        });
}

从 IndexedDB 中读取图片并显示

当需要显示图片时,可以从 IndexedDB 读取 Blob 数据并生成一个 URL 给 标签使用

typescript 复制代码
// 从 IndexedDB 中获取图片 Blob
function getCachedImage(url) {
    return new Promise((resolve, reject) => {
        openDatabase().then(db => {
            const transaction = db.transaction('images', 'readonly');
            const store = transaction.objectStore('images');
            const request = store.get(url);

            request.onsuccess = function(event) {
                if (event.target.result) {
                    resolve(event.target.result); // 返回图片 Blob
                } else {
                    reject('Image not found in cache');
                }
            };

            request.onerror = function() {
                reject('Failed to retrieve image from IndexedDB');
            };
        });
    });
}

// 使用缓存图片显示到页面
function displayImageFromCache(url, imgElementId) {
    getCachedImage(url)
        .then(blob => {
            const imgURL = URL.createObjectURL(blob);
            document.getElementById(imgElementId).src = imgURL;
        })
        .catch(error => {
            console.error('Error displaying cached image:', error);
        });
}

// 示例调用
const imageUrl = 'https://example.com/image.jpg';
fetchAndCacheImage(imageUrl); // 首次获取并缓存
displayImageFromCache(imageUrl, 'myImage'); // 从缓存中显示图片

LocalStorage缓存

typescript 复制代码
// 保存图片到 localStorage
function cacheImage(url) {
    fetch(url)
        .then(response => response.blob())
        .then(blob => {
            const reader = new FileReader();
            reader.onload = function () {
                localStorage.setItem('cachedImage', reader.result); // 将Base64数据存入localStorage
            };
            reader.readAsDataURL(blob);
        });
}

3、图片懒加载

懒加载和占位图也是提升前端图片加载性能的技巧,虽然不算是严格意义的缓存。懒加载会等到图片在视窗中出现时才开始加载,减少初始页面加载的资源量,占位图则在图片未加载完成前提供一个小尺寸、模糊的占位图,让页面看起来更平滑。

假设你有多个图片,添加 data-src 属性存储图片的实际 URL,src 属性设置为占位符(比如一张模糊的小图或空白图片),这样只有当图片进入视口时,才会加载实际的图片。

html 复制代码
<img class="lazy" data-src="https://example.com/photo1.jpg" src="placeholder.jpg" alt="Image 1" />
<img class="lazy" data-src="https://example.com/photo2.jpg" src="placeholder.jpg" alt="Image 2" />
<img class="lazy" data-src="https://example.com/photo3.jpg" src="placeholder.jpg" alt="Image 3" />
<!-- 更多图片 -->

使用 IntersectionObserver 来监视图片是否进入视口,如果进入视口,就把 data-src 的值赋给 src,从而触发图片加载。

typescript 复制代码
// 创建一个 IntersectionObserver 实例,监视图片是否进入视口
const lazyLoadImages = document.querySelectorAll('img.lazy');

const imageObserver = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
        // 如果图片进入视口
        if (entry.isIntersecting) {
            const image = entry.target;
            // 将 data-src 的真实图片 URL 赋值给 src 属性
            image.src = image.dataset.src;
            // 一旦图片加载完毕,停止观察该图片
            image.onload = () => {
                image.classList.remove('lazy'); // 可选:移除懒加载样式
            };
            observer.unobserve(image); // 停止观察已加载的图片
        }
    });
}, {
    rootMargin: '0px', // 可选:在视口边缘触发加载(当图片接近视口时开始加载)
    threshold: 0.1  // 可选:当图片至少 10% 显示时开始加载
});

// 观察所有懒加载图片
lazyLoadImages.forEach(image => {
    imageObserver.observe(image);
});

代码解释

IntersectionObserver:

创建 IntersectionObserver 实例来观察图片元素是否进入视口。IntersectionObserver 会异步监测指定元素与视口的交集变化。

entries 是一个包含所有监测元素的数组,entry.target 是当前进入视口的图片元素。

isIntersecting 属性表示图片是否已经进入视口(或者与视口重叠)。当该属性为 true 时,图片就进入了视口。

rootMargin:

rootMargin 是视口的扩展区域。当图片距离视口边缘一定距离时就触发加载。可以使用类似 CSS 的值(如 10px 0px)。

threshold:

threshold 定义了当多少比例的图片进入视口时触发懒加载,0.1 表示图片至少 10% 进入视口时触发加载。

image.onload:

一旦图片加载完毕,可以执行一些操作,比如移除懒加载样式(如 lazy 类),表示图片已成功加载。

observer.unobserve(image):

加载完图片后,停止观察该图片,优化性能。

使用 loading="lazy" 属性

使用 loading="lazy" 属性可以非常简单地实现图片的懒加载。浏览器就会在图片进入视口时自动加载它。这是原生浏览器对懒加载的支持,无需额外的 JavaScript 代码。这个属性已经被大多数现代浏览器支持。

html 复制代码
<img src="image1.jpg" alt="Image 1" loading="lazy" />
<img src="image2.jpg" alt="Image 2" loading="lazy" />
<img src="image3.jpg" alt="Image 3" loading="lazy" />

4、不同分辨率下使用不同的图片

html 复制代码
<picture>
  <source media="(max-width: 600px)" srcset="image-small.jpg">
  <source media="(max-width: 1200px)" srcset="image-medium.jpg">
  <img src="image-large.jpg" alt="Responsive Image">
</picture>

或者

html 复制代码
<img src="image-small.jpg" 
     srcset="image-small.jpg 500w, 
             image-medium.jpg 1000w, 
             image-large.jpg 2000w" 
     sizes="(max-width: 600px) 100vw, 
            (max-width: 1200px) 50vw, 
            33vw" 
     alt="Responsive Image">

5、使用webp格式的图片

使用 WebP 格式(如果浏览器支持)可以显著减小图片文件大小,从而提高加载速度。

可以通过 元素来根据浏览器支持选择图片格式。

html 复制代码
<picture>
    <source srcset="image.webp" type="image/webp">
    <img src="image.jpg" alt="Responsive Image">
</picture>

6、配置图片CDN

  1. 选择和设置 CDN 服务提供商
    首先,选择一个合适的 CDN 服务提供商。常见的 CDN 提供商包括:

Cloudflare

AWS CloudFront

Fastly

KeyCDN

Alibaba Cloud CDN

Tencent Cloud CDN

  1. 上传图片到 CDN
    选择图片存储方式:

存储在对象存储服务中:你可以使用云服务提供商的对象存储(如 Amazon S3、Alibaba OSS、腾讯云 COS)来存储图片文件,并通过 CDN 进行分发。通过这种方式,你可以确保文件安全,并允许 CDN 节点从这些存储服务获取资源。

直接通过 CDN 上传:有些 CDN 服务商允许直接将图片资源上传到其 CDN 网络。

上传图片:将图片上传到选定的存储服务(如 S3),并记录图片的 URL。

  1. 配置 CDN
    创建 CDN 加速域名:

登录你的 CDN 提供商控制台,创建一个加速域名(例如 cdn.yoursite.com)。

配置源站点(通常是你存储图片的服务器或对象存储服务)。源站点是 CDN 获取图片资源的地方。

设置缓存策略:

配置缓存时间(TTL,生存时间),指示 CDN 节点在没有更新时缓存图片的时间。一般情况下,静态资源(如图片)可以设置较长的缓存时间,如一周或一月。

启用 图片优化 功能(如果支持),以便根据设备类型和网络状况自动调整图片的大小和质量。

  1. 修改图片 URL

在网页中引用 CDN 提供的图片 URL。假设你的 CDN 域名是 cdn.yoursite.com,你可以像这样引用图片:

html 复制代码
<img src="https://cdn.yoursite.com/images/photo1.jpg" alt="Image 1" />
<img src="https://cdn.yoursite.com/images/photo2.jpg" alt="Image 2" />

将原本直接指向服务器的图片 URL 替换为指向 CDN 的图片 URL。

7、减少图片和动图的使用

这可能需要有较强的canvas功底,和较多的开发时间。但的确是能大幅度优化图片的方案。

能用css实现的简单动画,就别用动态图片。能用css实现的样式,就别用图片。

css样式如何优化?

1、关键css优先,非关键css等页面加载完成再加载或引入

在优化 CSS 加载时,遵循"先加载关键 CSS,再懒加载非关键 CSS"的策略,可以显著提升页面加载速度。

1、提取关键css

这就要分析一个页面的功能样式,是否可以分离为进入页面就要展示的css和等用户操作后才展示的css

css 复制代码
<style>
  /* 关键 CSS */
  body { margin: 0; font-family: Arial, sans-serif; }
  header { background-color: #333; color: white; padding: 10px; }
  /* 添加更多关键样式 */
</style>

2、 懒加载非关键 CSS

如果页面有复杂特效(如动画、hover 状态、特定 JavaScript 动画等),可以将这些 CSS 动效代码延迟到页面加载完成后再应用,从而加快页面初始渲染速度。通过在 标签中添加 media="print" 和 onload 事件来实现非阻塞加载。

html 复制代码
<link rel="stylesheet" href="non-critical.css" media="print" onload="this.media='all'">

3、使用 rel="preload" 优化 CSS 加载

对于需要优先加载的非关键 CSS,可以用 preload 指令提前加载,提高资源加载效率。

html 复制代码
<link rel="preload" href="important.css" as="style" onload="this.οnlοad=null; this.rel='stylesheet'">

除了上述处理非关键css,还可以使用js直接引入

js 复制代码
window.addEventListener('load', function() {
  let link = document.createElement('link');
  link.rel = 'stylesheet';
  link.href = 'additional-styles.css';
  document.head.appendChild(link);
});

2、使用 CSS 预处理器和后处理器

预处理器如 SCSS、LESS 可以更简洁地编写和管理 CSS。

后处理器如 PostCSS 可以根据浏览器的兼容性要求,自动添加前缀、压缩代码、处理变量等。

3、利用 CSS 变量

使用 CSS 变量可以减少重复代码,便于动态调整样式。比如色彩、尺寸等样式都可以用 CSS 变量来定义,从而减少代码量并优化可维护性。

css 复制代码
:root {
  --main-color: #4CAF50;
  --padding: 10px;
}

body {
  color: var(--main-color);
  padding: var(--padding);
}

4、减少和优化选择器的复杂性

尽量使用简单的选择器,避免使用深层嵌套或复杂选择器。深层嵌套和通配符(如 *)会增加浏览器的解析成本。

5、删除无用的 CSS

使用工具如 PurgeCSSUnCSS 来移除未使用的 CSS 代码。这样可以极大地减少 CSS 文件体积,提高加载速度。

6、利用缓存

为 CSS 文件设置长缓存时间。通过配置缓存策略,浏览器可以在多次访问时直接加载缓存中的 CSS 文件,避免重新请求 (前端的缓存策略,无非就是 Localstorage,service work)

7、编写代码时尽量减少重排和重绘

尽量避免频繁修改布局属性(如 width、height、padding),减少重绘和重排。可以使用 transform、opacity 等性能友好的属性,进行页面更新。

transform 调用的GPU来实现渲染 ,减少使用translate

相关推荐
Jinuss12 分钟前
Vue3源码reactivity响应式篇之computed计算属性
前端·vue3
落日沉溺于海12 分钟前
React From表单使用Formik和yup进行校验
开发语言·前端·javascript
知识分享小能手14 分钟前
React学习教程,从入门到精通, React 新创建组件语法知识点及案例代码(11)
前端·javascript·学习·react.js·架构·前端框架·react
会豪17 分钟前
工业仿真(simulation)--前端(五)--标尺,刻度尺
前端
会豪18 分钟前
工业仿真(simulation)--前端(四)--画布编辑(2)
前端
an__ya__20 分钟前
Vue数据响应式reactive
前端·javascript·vue.js
苦逼的搬砖工23 分钟前
Flutter UI Components:闲来无事,设计整理了这几年来使用的UI组件库
前端·flutter
想买Rolex和Supra的凯美瑞车主25 分钟前
Taro + Vite 开发中 fs.allow 配置问题分析与解决
前端
ruanCat26 分钟前
使用 vite 的 base 命令行参数来解决项目部署在 github page 的路径问题
前端·github
Codebee31 分钟前
使用Qoder 改造前端UI/UE升级改造实践:从传统界面到现代化体验的华丽蜕变
前端·人工智能