图片资源优化
图片压缩
图片压缩的方式有多种的,我们主要介绍以下几种:
imagemin
imagemin是一个用于压缩图片的优秀工具和插件集合。它可以通过各种算法和优化技术来压缩图片,包括无损压缩、有损压缩和格式转换等。imagemin支持多种图片格式,如JPEG、PNG、GIF和SVG等。
imagemin有很多针对不同图片格式的插件,你可以根据需要选择适当的插件来压缩图片。以下是一些常用的imagemin插件:
imagemin-jpegtran
用于优化和压缩JPEG图像。imagemin-pngquant
用于优化和压缩PNG图像,通过有损压缩算法来减小文件大小。imagemin--webp
用于优化和压缩WebP图像。它可以将其他图像格式(如JPEG、PNG、GIF)转换为WebP格式,以减小文件大小并提高加载性能。imagemin-gifsicle
用于优化和压缩GIF图像,可以删除冗余数据和调整颜色表等。imagemin-svgo
用于优化和压缩SVG图像,可以删除多余的元数据和无用标签等。
使用imagemin需要安装Node.js,并使用npm或yarn来安装imagemin及其各种插件。你可以在命令行中运行类似以下的命令:
npm install imagemin imagemin-jpegtran imagemin-pngquant imagemin-gifsicle imagemin-svgo
示例:
const imagemin = require('imagemin');
const imageminJpegtran = require('imagemin-jpegtran');
const imageminPngquant = require('imagemin-pngquant');
(async () => {
const files = await imagemin(['images/*.{jpg,png}'], {
destination: 'build/images',
plugins: [
imageminJpegtran(),
imageminPngquant({
quality: [0.6, 0.8]
})
]
});
console.log('压缩完成!');
})();
更多示例,参考
网站
- TinyPNG是一个在线图像压缩工具,专门用于压缩PNG图像。它可以减小PNG图像的文件大小,同时保持视觉质量的平衡。
- 需要注意的是,TinyPNG免费账号有一定的使用限制,包括每月可以压缩的图像数量和单个压缩图像的大小限制。如果需要更大的容量和额外的功能,可以考虑升级到付费账号。
- Kraken.io是另一个在线图像压缩工具,它能够帮助你压缩各种图像格式,包括JPEG、PNG和GIF等。Kraken.io提供了一些高级的压缩选项,并且还支持批量压缩和自动化集成。
- 需要注意的是,Kraken.io也有免费账号的使用限制,包括每月可压缩的图像数量和总压缩数据量的限制。如果你需要更大的容量和额外的功能,可以考虑升级到付费账号。
- Compressor.io是另一个流行的在线图像压缩工具,用于压缩JPEG、PNG、GIF和SVG等各种图像格式。它提供了简单易用的界面,可以帮助你压缩图像并减小文件大小。
- Compressor.io采用先进的压缩算法来减小图像文件大小,同时尽量保持图像质量。它还提供了一些额外的功能,如批量上传和批量压缩,以帮助你更高效地处理多个图像。
- 需要注意的是,Compressor.io的免费版有一些使用限制,包括每月可压缩的图像数量和每次上传的最大图片大小。如果你需要更大的容量和额外的功能,可以考虑升级到付费版。
除了上述的方法外,我们也可以利用图像处理软件(如Adobe Photoshop、GIMP等)来压缩图片;但是这需要有对应的专业技能;推荐使用imagemin;
小图片转 base64 格式
-
将图片转换为Base64格式可以将图片数据直接嵌入到HTML、CSS或JavaScript代码中,而无需进行外部文件的加载。
-
我们可以利用在线工具可用于将图片转换为Base64格式,例如 www.base64-image.de/或www.base64encode.net/base64-imag…。在这些网站上,您只需要上传图片并点击转换按钮,即可生成对应的Base64格式代码。
-
除了上述的方法外,也有其他方法可以将图片转换为 base64 格式;
-
需要注意的是,
将图片转换为Base64格式会增加数据的大小,会膨胀为原文件大小的 4/3,并且在代码中使用Base64格式的图片可能会增加代码的复杂性和可读性。因此,通常建议只将较小的图片或小量的必要图片转换为Base64格式,并谨慎使用。对于大量或较大的图片,仍建议使用外部文件,以便利用浏览器的缓存机制,并提高页面的加载性能。
cdn加载图片
-
对于移动端开发而言,由于屏幕宽度有限,完全没有必要去加载原图浪费带宽。一般图片都用 CDN 加载,可以计算出适配屏幕的宽度,然后去请求相应裁剪好的图片。
-
CDN(内容分发网络)是一种用于加速静态内容传输的网络架构。在前端中,你可以通过使用CDN来加载图片,以提高加载速度和性能。下面是一个加载图片并使用CDN的示例代码:
<!DOCTYPE html>
<html>
<head>
<title>CDN加载图片示例</title>
</head>
<body>
<img src="https://cdn.example.com/path/to/image.jpg">
</body>
</html>
在实际使用中,你需要将https://cdn.example.com/path/to/image.jpg
替换为你实际使用的CDN图片的URL。确保CDN地址正确并且有效,以便图片能够正确加载。
正确的图片格式
对于能够显示 WebP 格式的浏览器尽量使用 WebP 格式。因为 WebP 格式具有更好的图像数据压缩算法,能带来更小的图片体积,而且拥有肉眼识别无差异的图像质量,缺点就是兼容性并不好
小图使用 PNG,其实对于大部分图标这类图片,完全可以使用 SVG 代替
照片使用 JPEG
WebP 格式
- WebP是一种由Google开发的图像格式,旨在提供更高的图像压缩率和更好的图像质量。相比传统的JPEG和PNG格式,WebP通常可以减小图像文件大小,同时保持相似的视觉质量。
WebP图像格式的一些特点:
较小的文件大小
: WebP使用先进的压缩算法,可以在更小的文件大小下保存图像,从而提供更快的加载速度和更低的带宽要求。无损和有损压缩
: 在无损压缩的情况下,相同质量的WebP图片,文件大小要比PNG小26%;在有损压缩的情况下,具有相同图片精度的WebP图片,文件大小要比JPEG小25%~34%;透明度支持
: WebP可以处理图像的透明度信息,类似于PNG格式。这使得WebP成为处理需要透明背景的图像(如标志、图标等)的理想选择。动画支持
: 类似于GIF格式,WebP也可以支持动画图像,允许在单个文件中存储多个图像帧。
目前只有Chrome浏览器和Opera浏览器支持WebP格式,兼容性不太好。可以通过在不支持的浏览器中提供备用格式(如JPEG或PNG)来提升兼容性。
要将图像转换为WebP格式,你可以使用图像处理软件(如Adobe Photoshop、GIMP)或在线图像转换工具(如[Cloudinary](https://cloudinary.com)、[Imagify](<https://imagify.io/>))。
从上面可以看出,webp格式的图片前期转换成本较大,对于图片内容较少的项目,可以不使用;
精灵/雪碧图
- 精灵图(Sprite)是一种将多个小图标或图像合并到单张大图中的技术。通过将多个图标放置在一张图中,并使用CSS或其他方法来定位和显示特定的图标,可以减少HTTP请求次数,提高网页加载速度。
- 精灵图可以有效减少网页的加载时间和带宽使用,尤其适用于大量图标或小图像的场景,如图标字体、按钮、菜单、照片墙等。
- 需要注意的是,创建和维护精灵图可能需要一些额外的工作,特别是当你需要添加或修改图标时。因此,在使用精灵图时,建议根据具体情况综合考虑其优缺点。
video代替gif
- 使用视频来替代GIF图是一种常见的优化网页性能和用户体验的方法。GIF图在网页上加载和播放时,通常会占用较多的带宽和资源,并且不能提供高质量的图像或动画效果。而使用视频格式可以提供更高质量的图像和更流畅的动画效果,并且能够更好地进行压缩和优化。
以下是一些步骤和考虑因素,可以帮助你将视频用作GIF图的替代方案:
选择适当的视频格式
: 选择适当的视频格式是关键。常见的视频格式包括MP4、WebM和Ogg等。MP4是广泛支持的格式,适用于大多数现代浏览器。WebM和Ogg格式是开放的、自由的格式,适用于特定的浏览器和设备。优化视频文件大小
: 确保视频文件的大小尽可能小,以减少带宽和加载时间。使用视频编码器进行压缩,调整比特率、分辨率和质量设置等,以获得最佳的文件大小和视觉体验。自动播放和静音选项
: 在某些情况下,你可能希望视频自动播放。但请注意,自动播放的音频可能会打扰用户,因此最好将视频设置为静音播放。提供备用图像
: 对于不支持视频播放的设备或浏览器,可以提供备用图像。这可以通过使用<img>
标签和<video>
标签之间的回退机制来完成。实现懒加载
: 对于页面中的视频,可以使用懒加载技术来延迟加载视频。这可以通过使用JavaScript库(如Intersection Observer API)或手动编写脚本来实现。兼容性和测试
: 确保你选择的视频格式和播放方式在目标浏览器和设备上能够正常工作。进行兼容性测试,并注意处理浏览器兼容性问题。
- 通过将视频用作GIF图的替代方案,可以提供更好的视觉效果和用户体验,同时减少页面加载时间和带宽使用。
项目开发时
减少重排/重绘
重绘 (Redraw)
:某些元素的外观被改变所触发的浏览器行为(重新计算节点在屏幕中的绝对位置并渲染的过程); 例如:修改元素的填充颜色,会触发重绘;重排 (Reflow)
:重新生成布局,重新排列元素(重新计算各节点和css具体的大小和位置:渲染树需要重新计算所有受影响的节点 );例如:改元素的宽高,会触发重排;通过两者概念区别明显得知,重排要比重绘的成本大得多,我们应该尽量减少重排操作,减少页面性能消耗,而且重排一定会触发重绘,重绘不一定会触发重排。
减少重排(reflow)和重绘(repaint)是前端性能优化的重要方面之一。重排和重绘是浏览器渲染页面时的必要过程,但过度频繁的重排和重绘会导致性能下降。
哪些操作会导致重绘:
- color、background 相关属性:background-color、background-image 等
- outline 相关属性:outline-color、outline-width 、text-decoration
- border-radius、visibility、box-shadow
哪些操作会导致重排:
- 页面的首次渲染
- 浏览器的窗口大小发生变化
- 元素的内容发生变化
- 元素的尺寸或者位置发生变化
- 元素的字体大小发生变化
- 激活CSS伪类
- 查询某些属性或者调用某些方法
- 添加或者删除可见的DOM元素
优化建议:
- 操作DOM时,尽量在低层级的DOM节点进行操作
- 不要使用
table
布局, 一个小的改动可能会使整个table
进行重新布局 - 使用CSS的表达式
- 不要频繁操作元素的样式,对于静态页面,可以修改类名,而不是样式。
- 使用absolute或者fixed,使元素脱离文档流,这样他们发生变化就不会影响其他元素
- 避免频繁操作DOM,可以创建一个文档片段
documentFragment
,在它上面应用所有DOM操作,最后再把它添加到文档中 - 将元素先设置
display: none
,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘。 - 将DOM的多个读操作(或者写操作)放在一起,而不是读写操作穿插着写。这得益于
浏览器的渲染队列机制
使用 CSS transform
: 使用CSS transform属性(如translate、scale、rotate等)来实现动画和布局变化。CSS transform不会引起重排,因此可以提高性能。离线DOM
: 在需要频繁修改DOM时,可以使用离线DOM操作,即先将DOM节点从文档中移除,执行所有的修改操作后再添加回文档。这样可以避免浏览器的重排和重绘。使用虚拟DOM
: 一些前端框架(如React、Vue)使用虚拟DOM来减少对实际DOM的操作次数。虚拟DOM可以根据需要进行批量的DOM更新操作,从而减少重排和重绘的次数。
防抖/节流
防抖
防抖(Debouncing)是一种优化前端性能的技术,指在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。
这可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求。
函数防抖的实现
function debounce(func, delay) {
let timerId;
return function() {
const context = this;
const args = arguments;
clearTimeout(timerId); // 清除之前的定时器
// 设置定时器,使事件间隔指定事件后执行
timerId = setTimeout(() => {
func.apply(context, args);
}, delay);
};
}
可以通过如下方式使用防抖函数:
function handleInput() {
// 处理输入事件的操作
}
const debouncedHandleInput = debounce(handleInput, 300); // 创建防抖处理函数
inputElement.addEventListener('input', debouncedHandleInput); // 绑定防抖处理函数到输入事件
在上述代码中,handleInput
是处理输入事件的操作函数,debouncedHandleInput
是防抖处理函数,通过 debounce
函数包装 handleInput
函数得到。然后将防抖处理函数绑定到输入事件上,每次输入事件触发时,防抖函数会在指定的时间间隔内等待,直到没有再次触发事件时,执行操作函数 handleInput
。
通过使用防抖函数,可以有效地降低频繁触发事件所带来的计算、请求或渲染操作的次数,提高页面性能和用户体验。
防抖函数的应用场景:
- 按钮提交场景:防⽌多次提交按钮,只执⾏最后提交的⼀次
- 服务端验证场景:表单验证需要服务端配合,只执⾏⼀段连续的输⼊事件的最后⼀次,还有搜索联想词功能类似⽣存环境请⽤lodash.debounce
节流
节流(Throttling)是指规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。
节流可以使用在 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率。
函数节流的实现
function throttle(func, delay) {
let lastExecTime = 0;
let timerId;
return function() {
const context = this;
const args = arguments;
const currentTime = Date.now();
if (currentTime - lastExecTime < delay) {
clearTimeout(timerId); // 清除之前的定时器
timerId = setTimeout(() => {
lastExecTime = currentTime;
func.apply(context, args); // 执行操作
}, delay);
} else {
lastExecTime = currentTime;
func.apply(context, args); // 执行操作
}
};
}
可以通过如下方式使用防抖函数:
function handleScroll() {
// 处理滚动事件的操作
}
const throttledHandleScroll = throttle(handleScroll, 300); // 创建节流处理函数
window.addEventListener('scroll', throttledHandleScroll); // 绑定节流处理函数到滚动事件
在上述代码中,handleScroll
是处理滚动事件的操作函数,throttledHandleScroll
是节流处理函数,通过 throttle
函数包装 handleScroll
函数得到。然后将节流处理函数绑定到滚动事件上,每次滚动事件触发时,节流函数会根据设定的时间间隔来控制操作函数的执行频率。
通过使用节流函数,可以减少频繁触发事件所带来的计算、请求或渲染操作的次数,避免过多的操作对页面性能产生负面影响。
防抖函数的应用场景
:
拖拽场景
:固定时间内只执⾏⼀次,防⽌超⾼频次触发位置变动缩放场景
:监控浏览器resize动画场景
:避免短时间内多次触发动画引起性能问题
懒加载
懒加载(Lazy Loading)是一种前端性能优化技术,它可以将页面中的某些资源(如图片、视频、音频)推迟到用户需要时再进行加载,从而减少初始页面加载时间和数据传输量,提升页面加载速度和用户体验。
图片懒加载
图片懒加载(Lazy Loading Images)是一种常见的前端性能优化技术,它可以将页面中的图片加载推迟到用户需要访问或接近它们时再进行,从而减少初始页面加载时间和数据传输量,提升页面加载速度和用户体验。
在 Vue 中实现图片懒加载可以使用 vue-lazyload
这个第三方插件。下面是一个简单的示例:
- 安装
vue-lazyload
插件:
npm install vue-lazyload --save
- 在 Vue 项目的入口文件(一般是
main.js
)中导入和使用vue-lazyload
:
import Vue from 'vue';
import VueLazyload from 'vue-lazyload';
Vue.use(VueLazyload);
- 在需要懒加载图片的组件中,使用
v-lazy
指令来指定需要延迟加载的图片:
<template>
<div>
<img v-lazy="imageSrc" alt="Lazy Loaded Image">
</div>
</template>
<script>
export default {
data() {
return {
imageSrc: require('@/assets/image.jpg')
};
}
};
</script>
在这个示例中,v-lazy
指令绑定了 imageSrc
变量,该变量存储了图片的路径。当这个组件进入视口时,vue-lazyload
会自动将 imageSrc
的值赋给 src
属性,触发图片的加载。
需要注意的是,v-lazy
指令只能用于 <img>
元素上。
你也可以根据 vue-lazyload
的文档进一步自定义懒加载的行为,例如设置预加载图像、加载失败的占位图像、自定义加载效果等。
路由懒加载
路由懒加载(Route Lazy Loading)是一种在 Vue 中优化路由加载性能的技术。通过路由懒加载,可以将每个路由对应的组件代码拆分成多个小的代码块,在需要时再按需加载,从而实现更快的初始页面加载速度。
vue2
在 Vue 2 中,路由懒加载可以使用 import()
动态导入语法结合 Vue 的异步组件来实现。
下面是一个使用 import()
动态导入语法实现路由懒加载的示例:
在定义路由时,使用 import()
动态导入语法来指定路由组件的导入:
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter);
const routes = [
{
path: '/',
name: 'Home',
component: () => import('@/views/Home.vue') // 使用 import() 动态导入语法
},
{
path: '/about',
name: 'About',
component: () => import('@/views/About.vue') // 使用 import() 动态导入语法
}
];
const router = new VueRouter({
routes
});
export default router;
vue3
在 Vue 3 中,路由懒加载可以使用 import()
动态导入语法结合 Vue 的异步组件来实现。
下面是一个使用 import()
动态导入语法实现路由懒加载的示例:
- 在定义路由时,使用
import()
动态导入语法来指定路由组件的导入:
import { createRouter, createWebHistory } from 'vue-router';
const routes = [
{
path: '/',
name: 'Home',
component: () => import('@/views/Home.vue') // 使用 import() 动态导入语法
},
{
path: '/about',
name: 'About',
component: () => import('@/views/About.vue') // 使用 import() 动态导入语法
}
];
const router = createRouter({
history: createWebHistory(),
routes
});
export default router;
在这个示例中,import('@/views/Home.vue')
和 import('@/views/About.vue')
使用了 import()
动态导入语法,将路由组件的导入延迟到需要时才进行加载。
需要注意的是,在 Vue 3 中使用的是 createRouter
替代了 Vue 2 中的 VueRouter
,而 createWebHistory
则用于创建 web 历史路由。
- 在使用异步组件的路由组件时,要使用
defineAsyncComponent
创建异步组件:
import { defineAsyncComponent } from 'vue';
const Home = defineAsyncComponent(() => import('@/views/Home.vue'));
const About = defineAsyncComponent(() => import('@/views/About.vue'));
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: About
}
];
在这个示例中,defineAsyncComponent
用于将动态导入的路由组件包装成异步组件,以在需要时延迟加载。
通过路由懒加载,在 Vue 3 的项目中,可以将路由组件的加载延迟到需要时才进行,从而提高初始页面加载速度和性能。
组件懒加载
<script>
const dialogInfo = () => import(/* webpackChunkName: "dialogInfo" */ '@/components/dialogInfo');
export default {
name: 'homeView',
components: {
dialogInfo
}
}
</script>
适合组件懒加载的场景:
- 该页面的 JS 文件体积大,导致页面打开慢,可以通过组件懒加载进行资源拆分,利用浏览器并行下载资源,提升下载速度(比如首页)
- 该组件不是一进入页面就展示,需要一定条件下才触发(比如弹框组件)
- 该组件复用性高,很多页面都有引入,利用组件懒加载抽离出该组件,一方面可以很好利用缓存,同时也可以减少页面的 JS 文件大小(比如表格组件、图形组件等)
虚拟列表
虚拟列表(Virtual List)是一种优化技术,用于处理包含大量数据的列表或表格。它通过仅渲染当前可见的部分数据,而不是整个列表,来提高性能和用户体验。
传统的列表或表格组件通常会将所有数据一次性加载到DOM中,这可能导致渲染缓慢、占用大量内存,并且对于大规模数据集来说效果不佳。而虚拟列表则采取一种渐进式渲染的方式,只渲染当前视窗可见的部分数据,当用户滚动列表时,动态加载和卸载相应的数据。
虚拟列表的工作原理是根据列表容器的高度、行高和滚动位置计算当前可见的数据范围,然后仅加载和渲染该范围内的数据。通过这种方式,可以大大降低内存使用和渲染时间,从而提高应用程序的性能和反应速度。
虚拟列表可以在各种应用场景中使用,特别是在需要展示大量数据的情况下,例如数据表格、聊天记录、日历等。它能够有效地减少页面加载时间,提升用户体验,并且支持快速滚动和搜索等功能。
总结来说,虚拟列表是一种优化技术,通过动态加载和渲染可见部分数据,以提高大数据列表的性能和用户体验。
具体和示例请参考:关于虚拟列表,看这一篇就够了
部署压缩
Gzip
Gzip(GNU zip)是一种文件压缩算法和文件格式,常用于减少文件大小以提高网络传输速度。它采用无损压缩算法,能够将文件压缩成更小的体积,并在需要时进行解压缩还原为原始文件。
Gzip主要应用于Web服务器和浏览器之间的数据传输。当服务器响应浏览器请求时,可以使用Gzip将响应内容进行压缩,然后在传输过程中再进行解压缩。这样可以减少传输的数据量,加快网页加载速度,节省带宽消耗。
需要注意的是,Gzip压缩只能用于压缩文本数据,如HTML、CSS、JavaScript等。对于已经进行过压缩的文件(如图片、视频等),再进行Gzip压缩往往效果不明显,甚至可能导致压缩后的文件比原始文件还大。
在HTTP协议中,使用Gzip压缩的响应内容会在响应头部的Content-Encoding
字段中指定为”gzip”,告诉浏览器该响应已经经过压缩。
要在Webpack配置中启用Gzip压缩,可以使用compression-webpack-plugin
插件。该插件可以自动将输出的文件进行Gzip压缩。
首先,使用npm或yarn安装compression-webpack-plugin
:
npm install compression-webpack-plugin --save-dev
然后,在Webpack配置文件中导入该插件,并进行相关配置:
const CompressionPlugin = require('compression-webpack-plugin');
module.exports = {
// 其他配置项...
plugins: [
new CompressionPlugin({
filename: '[path][base].gz', // 压缩文件的命名规则
algorithm: 'gzip', // 使用gzip算法进行压缩
test: /.(js|css|html)$/, // 需要压缩的文件类型
threshold: 10240, // 超过10KB的文件才会被压缩(可根据需要调整)
minRatio: 0.8 // 压缩比例达到0.8才会被压缩(可根据需要调整)
})
]
};
上述配置中,我们使用CompressionPlugin
插件,并通过传递选项来指定了以下内容:
filename
: 指定压缩文件的命名规则,[path]
表示保留原路径,[base]
表示保留原文件名。algorithm
: 指定使用gzip算法进行压缩。test
: 指定需要压缩的文件类型。在示例中,我们指定了.js
、.css
和.html
文件。threshold
: 仅对超过10KB的文件进行压缩。可以根据需要调整该值。minRatio
: 指定压缩比例达到0.8时才会被压缩。可以根据需要调整该值。
配置完成后,运行Webpack构建命令,生成的输出文件将会自动进行Gzip压缩,并在同一目录下生成以.gz
为扩展名的压缩文件。
请注意,Gzip压缩需要服务器端支持并正确配置,以便解压缩递送给浏览器。你应该在服务器端设置相应的响应头(Content-Encoding: gzip
),以告知浏览器处理压缩过的文件。
以上是一个简单的Webpack Gzip压缩配置示例,你可以根据具体需求进行调整和优化。
具体和示例请参考:前端性能优化之Gzip
splitChunks
splitChunks是Webpack的一种功能,用于将代码分割成多个块,从而提高应用程序的性能。
通过使用splitChunks,可以将公共的依赖模块提取到单独的文件中,并在需要时进行异步加载,减少重复下载和提高缓存的效果。这样可以减小初始加载的文件大小,加快页面的加载速度。
在Webpack配置中,可以使用splitChunks配置项来配置代码分割的规则。以下是一个示例的splitChunks配置:
module.exports = {
// 其他配置项...
optimization: {
splitChunks: {
chunks: 'all', // 指定要处理的代码块类型,可选值为'all', 'async', 'initial'
minSize: 30000, // 拆分出的块的最小大小(单位为字节)
maxSize: 0, // 拆分出的块的最大大小(单位为字节),0表示无限制
minChunks: 1, // 拆分前共享模块的最小块数
maxAsyncRequests: 5, // 按需加载时并行请求的最大数量
maxInitialRequests: 3, // 入口点处并行请求的最大数量
automaticNameDelimiter: '~', // 文件名中生成的连接符
name: true, // 拆分块的名称
cacheGroups: {
vendors: {
test: /[\/]node_modules[\/]/, // 匹配需要拆分的模块
priority: -10 // 控制拆分模块的优先级
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true // 如果一个模块已经被拆分过了,可以重用它而不是再次生成新的拆分块
}
}
}
}
};
-
上述配置中,我们通过
optimization.splitChunks
指定了一系列参数和选项:chunks
:指定要处理的代码块类型。常用的值有’all’(所有代码块)、’async’(按需加载的代码块)和’initial’(初始加载的代码块)。minSize
和maxSize
:用于控制拆分出的块的大小范围。可以根据具体情况进行调整。minChunks
:指定拆分前共享模块的最小块数。如果一个模块在至少这么多个地方被引入,才会被拆分为独立的文件。maxAsyncRequests
和maxInitialRequests
:分别指定按需加载时并行请求的最大数量和入口点处并行请求的最大数量。automaticNameDelimiter
:指定生成的拆分块文件名的连接符。name
:为拆分块指定名称,设置为true表示自动生成名称。cacheGroups
:用于配置拆分块的缓存组。可以针对特定的模块匹配规则进行配置,如示例中的vendors
缓存组用于匹配来自node_modules目录的模块。priority
选项用于控制拆分模块的优先级,值越大优先级越高。
除了上述示例中的参数和选项外,还可以根据具体需求进行更复杂的配置,如配置多个缓存组、自定义匹配规则等。
通过合理配置splitChunks,可以有效减少重复代码的加载和传输量,提高应用程序的性能和用户体验。
Uglify
UglifyJS(简称为Uglify)是一个JavaScript代码压缩工具,它能够将JavaScript代码进行压缩和混淆,从而减小文件大小并提高加载性能。
使用Uglify可以实现以下功能:
代码压缩
:Uglify可以通过删除空格、注释、换行等无关紧要的字符来减小JavaScript文件的大小。代码混淆
:Uglify可以对变量名、函数名等进行重命名,使代码更难以理解和阅读。这可以增加代码的安全性,避免代码被逆向工程或盗用。Dead code removal(死代码清除)
:Uglify可以检测和删除不运行的代码,例如未被使用的变量、未到达的代码块等,从而减少文件大小并提高性能。
使用Uglify进行代码压缩、混淆和删除死代码可以大大减小JavaScript文件的大小,提高页面的加载速度,并减少网络传输的数据量。
Uglify有多种使用方式,包括命令行工具、构建工具如Webpack和Gulp的插件等。以下是一个使用Uglify的示例配置:
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
// ...
optimization: {
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
warnings: false,
compress: true,
mangle: true,
},
}),
],
},
// ...
};
上述示例中,我们在Webpack配置中使用了UglifyJsPlugin插件,并通过配置项uglifyOptions
来设置Uglify的选项,包括compress
(是否压缩代码)、mangle
(是否混淆代码)等。
需要注意的是,虽然Uglify可以减小文件大小和提高加载速度,但过度的混淆和压缩可能导致代码可读性降低,调试困难。因此,在使用Uglify进行代码压缩和混淆时,需要在性能和可维护性之间进行权衡。
Terser
Terser是一个JavaScript代码压缩工具,它可以将JavaScript代码进行压缩、混淆和删除死代码,以减小文件大小并提高加载速度。
Terser是UglifyJS v3的后继者,它的目标是提供更好的压缩效果和更快的压缩速度。
与UglifyJS相比,Terser在代码压缩和混淆方面提供了更高的压缩率和更强的混淆能力。它能够使用更短的变量名、重写代码结构和优化代码,使得压缩后的代码更小、更难以理解和逆向工程。
在Webpack中配置Terser的方法如下:
- 首先,确保已经安装了
terser-webpack-plugin
插件。你可以使用以下命令进行安装:
npm install terser-webpack-plugin --save-dev
- 在Webpack配置文件中引入
terser-webpack-plugin
:
const TerserPlugin = require('terser-webpack-plugin');
- 在你的Webpack配置对象中添加
optimization
属性,并在其中配置TerserPlugin:
module.exports = {
// ...其他配置项...
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
ecma: undefined,
warnings: false,
parse: {},
compress: {},
mangle: true,
module: false,
toplevel: false,
keep_classnames: undefined,
keep_fnames: false,
},
}),
],
},
};
在上述代码中,我们创建了一个TerserPlugin
实例,并将其作为minimizer
选项的值传递给Webpack的optimization
配置。通过terserOptions
字段,你可以指定Terser的各种配置选项。
需要注意的是,上述配置中只展示了一些常用的配置选项,你可以根据自己的需求进行调整和扩展。
这样配置后,Webpack构建时会自动使用TerserPlugin来压缩和混淆你的JavaScript代码。
Terser还提供了许多其他的选项,可以根据实际需求进行配置,如toplevel
(压缩顶级作用域中的符号)和mangle
(控制混淆选项)等。
在 Vue 项目中使用 Terser 的配置,可以通过在 vue.config.js
文件中进行以下设置:
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
// ...其他配置项...
configureWebpack: {
optimization: {
minimizer: [new TerserPlugin({
terserOptions: {
compress: {
drop_console: true,
},
// 其他 Terser 配置选项...
},
})],
},
},
};
上述代码中,我们使用了 configureWebpack
选项来配置 Webpack。通过在 optimization
对象中的 minimizer
数组中添加一个配置好的 TerserPlugin 实例,我们可以告诉 Webpack 使用该插件来压缩和混淆 JavaScript 代码。
需要注意的是,在进行JavaScript代码压缩时,应该谨慎地选择压缩选项。过度压缩和混淆可能导致代码的可读性降低,调试困难。因此,应在性能和可维护性之间进行权衡,并进行适度的压缩和混淆。
Tree shaking
Tree shaking是一种在打包过程中移除未使用代码的优化技术。它主要用于消除 JavaScript 或 TypeScript 带有模块化依赖的应用程序中的无用代码。
Tree shaking的原理是通过静态分析来确定在 bundle 中是否有一些模块没有被使用,然后将这些未使用的模块或导出从 bundle 中移除,以减少最终打包后的文件大小。
Tree shaking 的主要过程是通过以下步骤实现的
:
- 识别和构建模块依赖关系:通过分析模块之间的依赖关系,构建出一个模块依赖图。这可以通过模块化工具如Webpack、Rollup等自动处理。
- 标记未使用代码:对于每个模块,通过静态分析(静态编译时检测代码)来确定哪些代码没有被使用。
- 移除未使用代码:根据标记的结果,在打包过程中将未使用的模块或导出从 bundle 中移除,生成最终的压缩文件。
Tree shaking 可以大大减小最终打包后的文件大小,从而提高网页加载速度和性能。它适用于使用模块化开发并使用 ES6 模块规范的 JavaScript 或 TypeScript 应用程序。
需要注意的是,Tree shaking 只能消除无用的模块和导出,而不能消除无用的函数和变量。此外,Tree shaking 的效果还取决于模块的导出方式以及使用的打包工具和配置。
为了实现 Tree shaking,通常需要满足以下条件
:
- 使用 ES6 模块规范进行模块化开发。
- 使用支持 Tree shaking 的打包工具,如Webpack 2+、Rollup等。
- 配置打包工具的相关选项,如Webpack 中的
mode
设置为"production"
,以启用压缩和 Tree shaking 功能。
总体来说,Tree shaking 是一项强大的优化技术,可以显著减小打包后的文件大小,提升前端应用程序的性能。
具体和示例请参考:带你了解 Tree Shaking
模块外置
将模块外置(externalizing modules)是一种优化策略,它可以将项目中的某些模块从打包生成的文件中排除出去,并在运行时从外部加载这些模块。这样可以减小打包文件的大小,加快加载速度,并且可以利用浏览器的缓存机制。
在 Vue 项目中进行模块外置的配置方式如下:
- 首先,在
vue.config.js
文件中添加以下配置:
module.exports = {
// ...其他配置项...
configureWebpack: {
externals: {
// 将需要外置的模块名映射到全局变量
jquery: 'jQuery',
lodash: '_',
// 其他需要外置的模块...
},
},
};
- 在 HTML 文件中引入需要外置的模块的 CDN 版本,例如:
<!DOCTYPE html>
<html lang="en">
<head>
<!-- ...其他头部内容... -->
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
<!-- 其他外置模块的 CDN 引入... -->
</head>
<body>
<!-- ...页面内容... -->
<div id="app"></div>
<!-- ...其他底部内容... -->
</body>
</html>
在上述示例中,我们将 jQuery 和 Lodash 这两个模块配置为外置模块,然后在 HTML 文件的头部通过 CDN 引入它们的库文件。
这样配置后,在打包生成的文件中,相关模块的代码将不会被包含在内,而是在浏览器中通过全局变量(如 window.jQuery
、window._
)来使用这些外置模块。
请根据你的具体需求和使用的模块进行相应的配置。对于一些常用的库,你可以通过搜索其官方提供的 CDN 地址来引入。
缓存
缓存的原理就是更快读写的存储介质+减少IO+减少CPU计算=性能优化。而性能优化的第一定律就是:优先考虑使用缓存。
本地缓存
本地缓存是前端优化中常用的技术之一,它可以减少网络请求,加快页面加载速度,并提升用户体验。下面介绍几种常见的前端本地缓存技术:
LocalStorage
:LocalStorage 是 HTML5 提供的一种本地存储方案,可以将数据以键值对的形式存储在浏览器中。通过使用localStorage.setItem(key, value)
方法可以将数据存储到本地,使用localStorage.getItem(key)
方法可以从本地获取数据。LocalStorage 适用于存储较小量的数据,字符串格式。SessionStorage
:与 LocalStorage 类似,SessionStorage 也是 HTML5 提供的本地存储方案。不同之处在于 SessionStorage 存储的数据在当前会话(浏览器标签页)有效,关闭标签页后数据即被清除。SessionStorage 的 API 与 LocalStorage 相似,使用方法也类似。IndexedDB
:IndexedDB 是一种高级的、事务型的本地数据库解决方案,可以存储大量的结构化数据。IndexedDB 提供了丰富的 API,允许开发者创建和操作数据库,执行事务,存储和检索数据等。Cache API
:Cache API 是浏览器提供的一种缓存机制,用于缓存网络请求的响应结果。通过 Cache API 可以创建一个缓存对象,将请求的 URL 和响应结果保存起来。当再次请求相同的 URL 时,可以直接从缓存中读取响应,而无需再次发起网络请求。Service Worker
:Service Worker 是一种运行在浏览器后台的 JavaScript 脚本,可以拦截和处理网络请求。借助 Service Worker,可以实现高级的缓存策略,例如将需要频繁访问的资源缓存到本地,从而提供离线访问能力。
以上是几种常见的前端本地缓存技术,你可以根据具体需求选择合适的方案。请注意,在使用本地缓存时要注意数据的有效性和安全性,并合理利用缓存更新机制来保证数据的准确性。
浏览器缓存
浏览器缓存是前端优化中的重要策略,它可以减少重复请求,降低服务器负载,加快页面加载速度,并提升用户体验。以下是几种常见的浏览器缓存技术:
强缓存
:通过设置响应头中的Cache-Control
和Expires
字段来控制浏览器缓存。Cache-Control
可以设置缓存的有效期(秒),而Expires
则是一个具体的过期时间。当客户端再次请求相同资源时,如果缓存有效,则直接从缓存中读取,无需发送请求到服务器。协商缓存
:当缓存过期或者需要强制刷新时,可以使用协商缓存来判断资源是否被修改过。服务器在响应头中添加ETag
或Last-Modified
字段,用于标识资源的唯一性或最后修改时间。客户端再次请求资源时,会携带If-None-Match
(ETag)或If-Modified-Since
(Last-Modified)字段,与服务器进行比较。如果资源未发生变化,服务器返回 304 状态码,并告诉客户端可以使用缓存的版本。CDN 缓存
:内容分发网络(CDN)可以将静态资源缓存在分布式节点上,使用户可以从就近的节点获取资源,减少网络延迟。CDN 配置缓存机制时,可以利用强缓存和协商缓存策略,提高资源命中率,减少请求到源服务器的次数。Service Worker 缓存
:通过使用 Service Worker 技术,可以在浏览器中实现更高级的缓存策略。Service Worker 是一个独立于网页的 JavaScript 线程,可以拦截和处理网络请求。开发者可以在 Service Worker 中自定义缓存策略,将需要频繁访问的资源保存到缓存中,以提供离线访问能力。缓存 busting
:为了及时获取最新的资源版本,避免使用过期的缓存,可以通过缓存 busting 的方式来更新缓存。常见的方法包括在资源的 URL 中添加版本号、文件内容的哈希值等,每次更新资源时,修改对应的标识,从而使浏览器重新请求最新的资源。
以上是几种常见的浏览器缓存技术,可以根据具体需求选择合适的策略。在配置缓存时,需要注意缓存的有效性、更新机制以及对用户行为的灵活处理,以达到良好的缓存效果。
更多内容可以参考:前端优化之浏览器的缓存
网络
dns预解析
DNS预解析(DNS Prefetching)是一种浏览器机制,旨在提前解析网页中的域名,并将其映射到相应的IP地址。这样可以在用户点击链接之前,预先获取所需资源的IP地址,从而加速页面加载。
DNS预解析的优势包括:
- 加快网页加载速度:通过在浏览器后台进行DNS解析,可以减少DNS查找时间,加快资源获取的速度,提升用户体验。
- 预防延迟:当用户点击新链接时,浏览器能够快速获取资源的IP地址,避免了延迟和等待时间。
在HTML中,可以使用以下方法启用DNS预解析:
<link>
标签:
<link rel="dns-prefetch" href="//example.com">
通过在<head>
部分添加<link>
标签,并设置rel
属性为dns-prefetch
,指定要预解析的域名。
-
<meta>
标签:<meta http-equiv="x-dns-prefetch-control" content="on">
需要注意以下事项:
- 使用DNS预解析时,应仅预解析与当前页面相关的域名,避免无谓的消耗。
- 不同浏览器对DNS预解析的支持有所差异,部分旧版本浏览器可能不支持。
- DNS预解析涉及网络请求,如果页面上存在大量的DNS预解析域名,可能会增加网络负担。因此,在使用DNS预解析时应谨慎选择,并进行合理的测试和优化。
综上所述,DNS预解析通过提前解析域名并获取相应IP地址,加速网页加载过程。可以通过<link>
标签或<meta>
标签来实现DNS预解析,提高用户体验和页面加载速度。
http2
HTTP/2 是一种网络协议,作为 HTTP/1.1 的升级版,旨在提供更快、更高效的 Web 页面加载性能。它引入了一些新的特性和优化机制,以下是关于如何利用 HTTP/2 进行前端性能优化的几个方面:
- 多路复用(Multiplexing):HTTP/1.1 使用串行的方式发送请求和响应,而 HTTP/2 支持多个并发请求在同一个 TCP 连接上进行,通过帧和流的概念,实现了多个请求/响应的并行处理,减少了连接建立和关闭的开销,提升了效率。
- 二进制分帧(Binary Framing):HTTP/2 将数据划分为更小的二进制帧,每个帧携带一个帧头,这样可以更精确地控制数据传输,避免了传统的文本协议格式的解析开销,提高了传输效率。
- 首部压缩(Header Compression):HTTP/2 使用 HPACK 算法对请求和响应的首部进行压缩,减小了传输的数据量,节省了带宽。这一机制尤其针对重复出现的首部字段,例如 Cookie 或 User-Agent。
- 服务器推送(Server Push):HTTP/2 允许服务器主动推送与当前页面相关的资源给客户端,无需等待客户端发送请求。例如,当客户端请求 HTML 页面时,服务器可以主动推送与该页面相关的 CSS 和 JavaScript 文件,这样可以减少往返的请求数量,优化页面加载速度。
要充分利用 HTTP/2 进行前端性能优化,可以采取以下措施:
- 部署支持 HTTP/2 的服务器:确保你的网站使用的是支持 HTTP/2 的服务器软件,例如 Nginx、Apache 或 Node.js 等,并配置相应的证书和加密协议(通常使用 TLS)。
- 压缩资源和首部:减小资源的体积对于 HTTP/2 来说仍然很重要,压缩 CSS、JavaScript 和图像等静态资源,并通过合理的缓存策略减少重复的传输。
- 优化并行请求:由于 HTTP/2 支持多路复用,不再需要合并文件来减少请求数量。但仍可通过合理的拆分资源和域名分片等方式,实现更好的并行请求效果。
- 利用服务器推送:确定哪些资源可以通过服务器推送来提前发送给客户端,减少往返的请求数量,但要注意避免无谓的服务器推送和潜在的带宽浪费。
- 监测优化效果:使用工具和性能测试来监测和评估 HTTP/2 的性能优化效果,了解加载时间、带宽利用率等指标的变化,并进行适时的优化调整。
总的来说,HTTP/2 提供了多路复用、二进制分帧、首部压缩和服务器推送等机制,可以显著改善前端页面的加载性能。通过合理配置服务器、优化资源和并行请求,以及利用服务器推送等技术手段,可以充分发挥 HTTP/2 的优势,提升网站的用户体验。
更多信息请参考:前端性能优化 24 条建议(2020)
服务端
服务端渲染
渲染过程在服务器端完成,最终的渲染结果 HTML 页面通过 HTTP 协议发送给客户端,又被认为是‘同构’或‘通用’,如果你的项目有大量的detail页面,相互特别频繁,建议选择服务端渲染。
服务端渲染(SSR)除了SEO还有很多时候用作首屏优化,加快首屏速度,提高用户体验。但是对服务器有要求,网络传输数据量大,占用部分服务器运算资源。
Vue的Nuxt.js和React的next.js都是服务端渲染的方法。
我们或多或少都听过 Vue 写的 SPA 应用 SEO 不太好,为啥不好呢?实际上我们去搜索引擎搜一个问题时,搜索引擎会去“爬取”所有相关的内容,然后对内容进行排序,而爬虫爬取的是 HTML,我们之前说过,SPA 应用服务器响应的一般是只有根节点的容器,里面的内容在完成数据填充后才有,爬虫爬不到东西,自然不会优先显示你的网页,而通过 SSR 响应的 HTML 是 完整的 ,很 利于爬虫的采集。
除此之外,由于 SSR 请求返回的 HTML 是完整的,我们无需发起别的接口请求,因此 首屏渲染 会比 CSR 快上许多。
那么 SSR 什么时候该用呢?
首先应用如果比较注重 流量 的,推荐使用 SSR,比如 博客、论坛、官网 等,流量就是命脉。
像管理后台等 B 端网站,一般是没有必要使用服务端渲染的。
文章参考: