前端性能优化

331 阅读5分钟

优化方案

加载优化

资源压缩、合并

代码压缩

使用工具压缩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 脚本,使用 asyncdefer 属性。async 会使脚本在下载完成后立即执行,不阻塞页面渲染,但可能会打乱脚本执行顺序;defer 会在页面解析完成后按顺序执行脚本,适合不影响页面渲染的脚本。

渲染优化

减少重绘重排

批量操作 DOM

每次对 DOM 进行修改时,浏览器可能需要重新计算元素的布局(重排)和外观(重绘)。如果能将多次 DOM 修改合并为一次,就能减少重排和重绘的次数。

避免强制同步布局

当 JavaScript 读取需要计算的样式属性(如 offsetTopclientWidthgetComputedStyle 等)时,浏览器需要立即计算最新的布局,这可能会强制触发重排。如果在修改 DOM 样式后紧接着读取这些属性,就会导致不必要的重排。所以最好的做法是将读取操作和赋值操作分开进行,比如先进行所有的赋值, 然后在进行所有的读取

优化选择器

避免过度嵌套

选择器嵌套层级越深,浏览器匹配元素的计算量越大。尽量使用简单、直接的选择器。这个更多的是结合项目需求制定嵌套层级的规范。

避免通配符选择器

通配符选择器(*)会匹配页面上所有元素,性能开销较大,应尽量避免使用。

代码执行优化

js代码优化

避免全局变量

全局变量会增加命名空间冲突的风险,且访问速度相对较慢。尽量将变量定义在局部作用域内。

减少闭包使用

闭包会使内部函数引用外部函数的变量,导致这些变量无法被垃圾回收机制回收。过度使用闭包可能会造成内存泄漏。

算法优化

避免不必要的循环嵌套

使用高效的排序算法

对于多次用到的计算,尽量缓存结果

结构优化

正确使用数组和json

学会使用Map和Set

Web Workers

对于计算密集型的任务,使用 Web Workers 在后台线程中执行,避免阻塞主线程,保证页面的流畅性。主线程和 Web Worker 之间通过消息传递机制postmessage进行通信。

其他优化补充

还有一些其他的优化方案

  1. 字体优化

这个其实也可以划分到加载优化中,选择合适的字体库或者抽取需要的字体编码,减小字体的体积,提高加载效率和渲染效率。但项目中这个一般多少都会进行妥协

  1. 虚拟dom

目前前端的主流框架基本上都内部实现,所以正常项目中一般也不会专门在这块进行处理