阅读 5471

前端性能优化总结

1.减少HTTP请求

一个完整的 HTTP 请求需要经历 DNS 查找,TCP 三次握手,浏览器发出 HTTP 请求,服务器接收、处理请求并返回响应,浏览器接收响应等过程。建立连接和下载数据都需要时间,应尽量将多个小文件合并为一个大文件,从而减少 HTTP 请求次数。

HTTP2.0的多路复用和服务器推送功能解决了HTTP多次请求的问题,HTTP2.0逐渐普及了,这个方法也不那么侧重了。

2.将 CSS 放在文件头部,JavaScript 文件放在底部

CSS 放在文件头部,是因为先加载 HTML 再加载 CSS,用户第一时间看到的页面是没有样式的,为了避免这种情况发生,就要将 CSS 文件放在头部了。
JS文件放在底部,是为了避免JS文件的加载阻塞DOM的解析和渲染。如果想把script标签放在头部,加上 defer 属性就可以了,异步下载,延迟执行。 script 标签加上 async 属性可能会阻塞渲染。
关于defer和async属性的差别可以看这篇:图解 script 标签中的 async 和 defer 属性

3.减少重绘重排

浏览器渲染过程

  • 构建对象模型(DOM树,CSSOM)

  • 构建渲染树(RenderTree)

  • 布局:计算节点位置大小信息

  • 渲染:GPU将图层合并并显示在屏幕上

    image.png

重排
元素位置和尺寸发生改变、DOM元素的增删、添加行内样式、浏览器窗口发生改变、页面首次渲染,元素相对于文档定位改变时会导致浏览器重新生成渲染树,这个过程叫重排。

重绘
当重新生成渲染树后,会将渲染树每个节点绘制到屏幕,这个过程叫重绘。颜色改变、背景图片改变,只要不影响元素相对于浏览器文档位置的改变,就只导致重绘。

如何减少重排重绘?

  • 减少DOM操作 (修改DOM节点的多条语句合并成一条语句来执行:事件代理)
  • 避免使用行内样式/样式集中改变
  • 使用 absolute 或 fixed 脱离文档流
  • 优化动画 (将动画效果应用到脱离文档流的元素上;使用css过渡/动画属性;画布等)

4.图片优化

图片延迟加载

在页面中,当图片出现在浏览器的可视区域时,才设置图片的src属性,加载图片。
懒加载相当于是把代码在一些逻辑断点处分离开,需要加载时才加载。这样加快了应用的初始加载速度,减轻了它的总体体积,因为某些代码块可能永远不会被加载。

懒加载实现原理可以看这篇:前端性能优化之图片懒加载

使用字体图标 iconfont 代替图片图标

字体图标就是将图标制作成一个字体,使用时就跟字体一样,可以设置属性,例如 font-size、color 等等,非常方便。并且字体图标是矢量图,不会失真。并且生成的文件特别小,很好用。比如阿里的图标库。

使用精灵图

所谓精灵图就是把很多的小图片合并到一张较大的图片里,所以在首次加载页面的时候,只需要加载大的精灵图,这样在一定程度上减少了页面的加载速度,也一定程度上缓解了服务器的压力。
使用:将精灵图设为一个大背景,然后通过background-position来移动背景图,从而显示出我们想要显示出来的部分。
缺点:图片的背景需要详细测量而得出来的,当需要改动页面,会很麻烦。适配也容易出现问题,现在用的很少了。

尽可能利用 CSS3 效果代替图片

有很多图片使用 CSS 效果(渐变、阴影等)就能画出来,这种情况选择 CSS3 效果更好,动画也可以使用css过渡/动画属性/画布等方法来优化,可以把动画的效果和方法绑定在脱离文档流的元素上,即减小了代码量又可避免引起重排。

  • 动画animation 与 transition 比较
    • transition只能定义开始状态和结束状态,需要事件触发(一次)
    • animation需要带keyframes的名字来描述动画,可以通过from A to B或者百分比来规定变化发生的时间
  • 矢量图形svg 和画布canvas 的比较
    • svg是基于XML格式的矢量图形,基于网格,放大缩小不会失真,可以用css和js操作,支持事件处理
    • canvas是基于像素渲染,放大缩小会失真,通过js进行绘制,绘制的图像都在画布里,一旦图形被绘制完成,就不会再得到浏览器的关注,不支持事件绑定。

使用 webp 格式的图片

首先了解一下不同格式的图片:

  • JPEG(或JPG): 此格式非常适合具有多种颜色的图像,并且是静止图像的首选选项。但是,该格式的图片压缩后图像质量略有下降。
  • PNG : 即使图像被压缩,也可以保持图像质量。此格式适用于具有大量数据或像素的图像,例如徽标,大量文本图像和插图。但是,它不适用于照片。
  • WebP : 它具有更优的图像数据压缩算法,图片尺寸更小,又保留了图像质量(肉眼识别无差异);同时具备了无损和有损的压缩模式、Alpha 透明以及动画的特性,在 JPEG 和 PNG 上的转化效果都挺好的。

了解webp可以看这篇:图片格式WEBP全面解析 推荐一个把转化成WebP格式图片的网站:又拍云

webp图片的生产可以看这篇:webp图片的优劣势及生成

5.压缩文件

压缩文件可以减少文件下载时间,让用户体验性更好。

gzip 是目前最流行和最有效的压缩方法,可以通过向 HTTP 请求头中的 Accept-Encoding 头添加 gzip 标识来开启这一功能。

在 webpack 可以使用如下插件进行压缩:

  • JavaScript:UglifyPlugin
  • CSS :MiniCssExtractPlugin
  • HTML:HtmlWebpackPlugin

6.善用缓存,不重复加载相同资源

为了避免用户每次访问网站都得请求文件,我们可以将已请求到的资源缓存在浏览器,再次请求时,可以从缓存中读取未改变的资源,避免重新下载资源耗费时间。
这里又涉及HTTP缓存(强缓存和协商缓存)的知识,可以看这篇: 一文读懂http缓存

题外话:关于再次请求的资源信息可以F12查看Network,状态码和资源缓存的位置可参看下面内容

image.png

资源储存位置统计表

状态码size类型说明
200form memory cache不请求网络资源,资源在内存当中
200form disk ceche不请求网络资源,资源在磁盘当中
200资源大小数值从服务器下载最新资源
304报文大小请求服务端发现资源没有更新,使用本地资源

7.使用事件代理

事件代理(委托):把原本需要绑定在子元素的响应事件委托给父元素,让父元素担任事件监听的职务。原理是DOM元素的事件冒泡。 优点:节省内存空间,减少事件注册(比如鼠标事件和键盘事件,ul代理所有li的click事件,新增元素时无需再次对其进行事件绑定)。

事件代理具体实现可以看这篇文档:JavaScript事件代理(事件委托)

8.静态资源使用CDN

内容分发网络(CDN)是一组分布在多个不同地理位置的web服务器。这些服务器存储着数据的副本,因此服务器会根据哪些服务器离用户距离最近来满足数据的请求。 CDN的两个核心点:

  • 缓存:就是指我们把资源复制一份到CDN服务器的这个过程
  • 回溯:指CDN发现自己没有这个资源(一般是缓存的数据过期了),转头向根服务器(或者上级服务器)去要资源的这个过程

CDN原理:CDN原理

9.使用服务器端渲染

客户端渲染: 获取HTML文件,根据需要下载 JavaScriptCSS文件,运行文件,生成 DOM,再渲染。

服务端渲染:服务端返回 HTML 文件,客户端只需解析 HTML

  • 优点:首屏渲染快,SEO好。
  • 缺点:配置麻烦,增加了服务器的计算压力

区别:客户端渲染的网站是直接返回HTML文件,而服务端渲染的网站是渲染完页面再返回这个HTML文件。VueSSR就是服务端渲染。

题外话
单页面应用(SPA)就是客户端渲染,页面加载完后不会在重新加载或跳转,取而代之是利用JS动态变换来响应用户的操作。
SPA具有良好的交互体验,没有页面的切换,不会出现"白屏","闪烁"现象;良好的前后端工作分离模式;并且减轻服务器的压力。但首屏加载慢,不利于SEO(搜索引擎优化),不适合开发大型项目。

可以看出,各有各的好处和缺点。服务端渲染配置麻烦,会加重服务器的压力,其实实际项目中的话,可以异步加载组件,或者使用vue-router懒加载组件。

前端渲染、服务器端渲染、同构渲染

同构渲染简单来说就是一份代码,服务端先通过服务端渲染(server-side rendering,下称ssr),生成html以及初始化数据,客户端拿到代码和初始化数据后,通过对html的dom进行patch和事件绑定对dom进行客户端激活(client-side hydration,下称csh),这个整体的过程叫同构渲染。其实就是满足三个条件:1. 同一份代码 2. ssr 3. csh

同构渲染相关问题: 同构渲染

可以看这篇:前端渲染、服务器端渲染、同构渲染

10.防抖节流

在前端开发中会遇到一些频繁的事件触发,比如:

  • window 的 resizescroll
  • mousedownmousemove
  • keyupkeydown

利用防抖和节流来解决。

防抖

  • 在事件被触发的n秒后再执行回调,如果再n秒内又被触发,再重新计时
    function debounce(func,time) {
        let timer = null;
        return () => {
           clearTimeout(timer);    // timer形成闭包;**防抖重在清零**
           timer = setTimeout(() => {
              func.apply(this, arguments);   // 用箭头函数确保上下文环境的this
           }, time);
        }
    }
    复制代码
  • 应用场景
    • 1.登录、发短信等按钮避免用户点击太快,以致于发送了多次请求,需要防抖
    • 2.调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖
    • 3.文本编辑器实时保存,当无任何更改操作n秒后进行保存

节流

  • 规定在一个单位时间内,只能触发一次函数,如果在该单位时间内多次触发,只一次生效
    //  时间戳版本
    function throtte(func, time){
        let preTime = 0;
        return () => {
            const nowTime = Date.now();
            if(nowTime - preTime >= time){
                func.apply(this, arguments);
                preTime = Date.now();
            }
        }
    }
    // 定时器版本
    function throttle(func, delay) {
        var timer = null;
        return function () {
            if (!timer) {
                func.apply(this, arguments);
                timer = setTimeout(() => {
                    timer = null;
                }, delay);
            } else {
                console.log("上一个定时器尚未完成");
            }
        }
    }
    复制代码
  • 应用场景
    • 1.scroll 事件,每隔1秒计算一次位置信息等;监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断
    • 2.浏览器播放事件,每隔1秒计算一次进度信息等
    • 3.input 框实时搜索并发送请求展示下拉列表,每隔1秒发送一次请求 (也可做防抖)

11.vue组件中使用keep-alive保存组件状态

keep-alive是一个抽象组件,自身不会渲染一个DOM元素,也不会出现在父组件链中;使用keep-alive包裹动态组件时,会缓存不活动的组件实例,而不是销毁他们。

应用场景: 用户在某个列表页面选择筛选条件过滤出一份数据列表,由列表页面进入数据详情页面,再返回该列表页面,我们希望:列表页面可以保留用户的筛选(选中)状态。keep-alive就是用来解决这种场景问题。当然keep-alive不仅仅是能够保存页面/组件的状态这么简单,它还可以避免组件反复创建和渲染,有效提升系统性能。 总的来说,keep-alive用于保存组件的渲染状态

vue相关知识点可以看这篇:Vue必须掌握的知识点你都了解了吗?

12.webpack按需加载代码——Tree-Shaking

懒加载或者按需加载,是一种很好的优化网页或应用的方式。这种方式实际上是先把你的代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用或即将引用另外一些新的代码块。这样加快了应用的初始加载速度,减轻了它的总体体积,因为某些代码块可能永远不会被加载。

在 webpack 项目中,有一个入口文件,相当于一棵树的主干,入口文件有很多依赖的模块,相当于树枝。实际情况中,虽然依赖了某个模块,但其实只使用其中的某些功能。通过 tree-shaking,将没有使用的模块摇掉,这样来达到删除无用代码的目的。(ES6的import动态引入组件实现按需加载)

Tree-Shaking性能优化实践 - 原理篇

13.使用 Web Workers

Web Worker 的作用,就是为 JS 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。

Web Worker适用于那些处理纯数据,或者与浏览器 UI 无关的长时间运行脚本,这样主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢。

Web Worker的使用可以看这篇:使用 Web Workers 来加速 JavaScript 应用

最后:写这篇文章,是从面试回答问题上来简洁总结的,一扩展就太冗杂了。每个部分附了相关文档链接,有兴趣可以扩展了解,动手实现,才能更深入地了解。祝大家都越来越好呀~有很多不足的地方,欢迎指正。

14.Vue首屏优化

Vue首屏加载速度优化,提升80%以上

参考文档: 前端性能优化24条建议

文章分类
前端
文章标签