优化方案
加载优化
资源压缩、合并
代码压缩
使用工具压缩js、css代码,去除代码中的空格、注释,优化代码中的变量名称(简化、缩短),减小代码体积。
图片压缩
在保证图片视觉质量的同时压缩图片文件大小,对于 JPEG 图片,可适当降低画质;PNG 图片则通过优化算法减少颜色数量。
文件合并
代码文件合并一般借助工具比如Webpack,其本身会自动将所有依赖的 js 模块打包合并成一个或多个 bundle 文件,css文件可以借助相关插件实现该功能。只需要正确配置好入口文件和模块的引用关系。
图片文件合并的本质是制作雪碧图,总体目的都是减少浏览器请求次数。
但要注意避免合并过大的文件,以免影响加载性能。
实现工具 - webpack
js 压缩合并
安装插件
npm install webpack webpack-cli terser-webpack-plugin --save -dev
配置插件
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
//...其他配置
optimization: {
minimizer: [
new TerserPlugin()
]
}
};
CSS 压缩合并
安装插件
npm install css-mini-extract-plugin optimize-css-assets-plugin --save -dev
配置插件
const MiniCssExtractPlugin = require('css-mini-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-plugin');
module.exports = {
//...其他配置
module: {
rules: [
{
test: /.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
}
]
},
optimization: {
minimizer: [
new OptimizeCSSAssetsPlugin({})
]
},
plugins: [
new MiniCssExtractPlugin()
]
};
图片压缩
安装插件
npm install image -webpack- loader --save -dev
配置插件
module.exports = {
//...其他配置
module: {
rules: [
{
test: /.(png|jpg|jpeg|gif)$/i,
use: [
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
progressive: true,
quality: 65
},
// optipng.enabled: false will disable optipng
optipng: {
enabled: false
},
pngquant: {
quality: [0.65, 0.90],
speed: 4
},
gifsicle: {
interlaced: false
},
// the webp option will enable WEBP
webp: {
quality: 75
}
}
}
]
}
]
}
};
资源缓存
强制缓存
通过设置 HTTP 头 Cache-Control 和 Expires 来实现。Cache-Control 优先级高于 Expires,例如设置 Cache-Control: max-age = 31536000(一年),表示资源在一年内再次请求时,直接从浏览器缓存中获取,无需向服务器发送请求。
协商缓存
使用 Last-Modified/If-Modified-Since 或 ETag/If-None-Match 这两组 HTTP 头。浏览器首次请求资源时,服务器返回 Last-Modified 或 ETag 标识资源的最后修改时间或唯一标识。再次请求时,浏览器带上 If-Modified-Since 或 If-None-Match,服务器对比后决定是否返回新资源。
按需加载
动态加载
在构建工具中,比如webpack中的动态导入(import())实现代码分割。对于大型单页应用,将路由组件或功能模块分割成多个文件,只有在需要时才加载。
图片懒加载
对于页面中的图片,尤其是在视口外的图片,延迟加载。可以通过监听 scroll 事件,判断图片是否进入视口,再加载图片。也可以使用 loading="lazy" 属性(现代浏览器支持)来实现原生的图片懒加载。
资源加载顺序
关键 CSS 优先加载
将关键路径 CSS(即页面渲染首屏所需的 CSS)放在 <head> 标签内尽早加载,确保页面样式能尽快应用,避免无样式内容闪烁。
脚本异步加载
对于 JavaScript 脚本,使用 async 或 defer 属性。async 会使脚本在下载完成后立即执行,不阻塞页面渲染,但可能会打乱脚本执行顺序;defer 会在页面解析完成后按顺序执行脚本,适合不影响页面渲染的脚本。
渲染优化
减少重绘重排
批量操作 DOM
每次对 DOM 进行修改时,浏览器可能需要重新计算元素的布局(重排)和外观(重绘)。如果能将多次 DOM 修改合并为一次,就能减少重排和重绘的次数。
避免强制同步布局
当 JavaScript 读取需要计算的样式属性(如 offsetTop、clientWidth、getComputedStyle 等)时,浏览器需要立即计算最新的布局,这可能会强制触发重排。如果在修改 DOM 样式后紧接着读取这些属性,就会导致不必要的重排。所以最好的做法是将读取操作和赋值操作分开进行,比如先进行所有的赋值, 然后在进行所有的读取
优化选择器
避免过度嵌套
选择器嵌套层级越深,浏览器匹配元素的计算量越大。尽量使用简单、直接的选择器。这个更多的是结合项目需求制定嵌套层级的规范。
避免通配符选择器
通配符选择器(*)会匹配页面上所有元素,性能开销较大,应尽量避免使用。
代码执行优化
js代码优化
避免全局变量
全局变量会增加命名空间冲突的风险,且访问速度相对较慢。尽量将变量定义在局部作用域内。
减少闭包使用
闭包会使内部函数引用外部函数的变量,导致这些变量无法被垃圾回收机制回收。过度使用闭包可能会造成内存泄漏。
算法优化
避免不必要的循环嵌套
使用高效的排序算法
对于多次用到的计算,尽量缓存结果
结构优化
正确使用数组和json
学会使用Map和Set
Web Workers
对于计算密集型的任务,使用 Web Workers 在后台线程中执行,避免阻塞主线程,保证页面的流畅性。主线程和 Web Worker 之间通过消息传递机制postmessage进行通信。
其他优化补充
还有一些其他的优化方案
- 字体优化
这个其实也可以划分到加载优化中,选择合适的字体库或者抽取需要的字体编码,减小字体的体积,提高加载效率和渲染效率。但项目中这个一般多少都会进行妥协
- 虚拟dom
目前前端的主流框架基本上都内部实现,所以正常项目中一般也不会专门在这块进行处理