Deng
Deng
Web前端性能优化(二) | odjBlog
    欢迎来到odjBlog的博客!

Web前端性能优化(二)

技术总结及问题解决 odjbin 5个月前 (08-02) 29次浏览 0个评论

资源合并与压缩

  • 理解减少 http 请求数量减少请求资源大小两个优化要点
  • 掌握压缩于合并的原理
  • 掌握通过在线网站和 fis3 两种实现压缩与合并的方法

http 清求的过程及潜在的性能优化点

深入理解 http 请求的过程 是前端性能优化的核心

  • dns 是否可以通过缓存减少 dns 查询时间 ?
  • 网络请求的过程走最近的网络环境 ?
  • 相同的静态资源是否可以缓存 ?
  • 能否减少请求 http 请求大小 ?
  • 减少 http 请求
  • 服务端渲染

html 压缩

HTML 代码压缩就是压缩这些在文本文件中有意义,但是在 HTML 中不显示的字符,包括空格,制表符,换行符等,还有一些其他意义的字符,如 HTML 注释 也可以被压缩。

  • 使用在线网站进行压缩
  • nodejs 提供了 html-minifier 工具等 npm 工具
  • 后端模板引擎渲染压缩

HTML 优化

  • 减少 iframes 使用
  • 压缩空白符, 空行
  • 避免节点深层级嵌套 div 盒子
  • 避免 table 布局
  • 删除注释
  • css&Javascript 尽量外链
  • 删除元素默认属性
  • 单闭合标签 li img 不用闭合?

CSS 压缩

  • 使用在线网站进行压缩
  • html-minifier 工具对 html 中的 css 进行压缩
  • 使用 clean-css 对 css 进行压缩

CSS 对性能的影响

nth-child

css 优化

  • 降低 CSS 对渲染的阻塞, 先加载首屏资源
  • 利用 GPU 进行完成动画
  • 使用 li{contain: layout}?还有?
  • 使用 font-display 属性

Js 压缩与混乱

  • 无效字符的删除
  • 剔除注释
  • 代码语义的缩减和优化
  • 代码保护

如何操作:

  • 使用在线网站进行压缩
  • html-minifier 工具对 html 中的 js 进行压缩
  • 使用 uglifyjs2 对 js 进行压缩
  • 使用 WebPack 对 JS 在构建时压缩

文件合并

存在的问题:

  • 首屏渲染问题
  • 缓存失效问题

建议:

  • 公共库合并

  • 不同页面的 js 分别合并( 针对单页面, 异步加载组件 )

  • 见机行事, 随机应变

  • 使用在线网站进行文件合并

  • 构建阶段使用 nodejs 实现文件合并

使用 fis3 构建工具自动压缩合并

图片优化

图片优化- 图片加载优化

  • 原生的图片懒加载方案 < img loading="lazy"
  • 第三方图片懒加载方案
    verlok/lazyload
    yall.js
    Blazy

    使用渐进式图片

使用响应式图片

  • Srcset 属性的使用
  • Sizes 属性的使用
  • picture 的使用

不同格式图片常用的业务场景

  • jpg 有损压缩, 压缩率高, 不支持透明
    • 优点: 压缩比很高, 颜色色彩画质好。缺点: logo 不用 jpeg,边缘感粗糙
    • 使用场景: 轮播图
  • png:优点: 支持透明背景图片(线条, 边缘, 细腻程度),浏览器兼容好。缺点: 保留细节, 体积大【小的图片. 图标 logo】
  • webp 压缩程度更好, 在 ios webview 有兼容性问题 ( 安卓全部 )
  • svg 矢量图, 内容内嵌, 相对较小, 图片样式相对简单的场景 ( logo )

css 雪碧图

  • 减少网站 http 请求数量
  • 缺点: 整合图片比较大时, 一次加载比较慢

image inline

  • 将图片的内容内嵌到 html 当中
  • 减少网站 http 请求数量

矢量图

  • svg 进行矢量图的绘制
  • 使用 iconfont 解决 icon 问题

在安卓下使用 webpp

  • WebP 的优势体现在它具有更优的图像数据压缩算法,能带来更小的图片体积,而且拥有肉眼识别无差异的图像质量;
  • 同时具备了无损和有损的压缩模式、Alpha 透明以及动画的特性,在 JPEG 和 PNG 上的转化效果都非常优秀、稳定和统一。
  • 比 png 体积更小
  • ie 不支持 兼容性不好, 80%

css 和 js 的装载与执行

HTML 页面加载渲染的过程

  • 理解浏览器端 html, css, js 的加载过程
  • 结合 chrome 的能力学习掌握 css, js 加载过程中的优化点

特点

  • 顺序执行, 并发加载
    • 词法分析
    • 并发加载
    • 并发上限
  • 是否阻塞
  • 依赖关系
  • 引入方式

css 阻塞?

  • css head 中阻塞页面的渲染
  • css 阻塞 js 的执行
  • css 不阻塞外部脚本的加载

js 阻塞?

  • 直接引入的 js 阻塞页面的渲染
  • js 不阻塞资源的加载
  • js 顺序执行, 阻塞后续 js 逻辑的执行

懒加载与预加载

懒加载原理

  • 图片进入可视区域之后请求图片资源(动态图片的 src 的地址是否真实的, 否: 可能为 1px 占位符) image 标签的 src 被设置后, 就会请求资源
  • 对于电商等图片很多,页面很长的业务场景适用
  • 减少无效资源的加载
  • 并发加载的资源过多会阻塞 js 的加载,影响网站的正常使用

预加载原理

  • 图片等静态资源在使用之前的提前请求
  • 资源使用到时能从缓存中加载,提升用户体验
  • 页面展示的依赖关系维护

懒加载原生 js 和 zepto.lazyload

需要监听 scroll 事件,在 scroll 事件的回调中,去判断我们的懒加载的图片是否进入可视区域

预加载原生 js 和 PreloadJS 实现

  • 1.使用 img 标签,style 样式 display: none

  • 2.使用 Image 对象
    var image=new Image()
    image.src="https://xxxx"

  • 3.使用 XMLHttpRequest 对象

  • 4.使用 PreloadJS
    new createjs.LoadQueue(false)
    ??为啥要传参 false

二者本质上是控制图片加载的时机

权衡浏览器本身具有加载资源的能力,让这能力饱和,充分使用,好让前端性能优化,有更好的体验

重绘和回流

回流与重绘,如何避免布局抖动

布局和绘制

  • 渲染树只包含网页需要的节点
  • 布局计算每个节点精确的位置和大小 -“盒模型”
  • 绘制是像素化每个节点的过程

避免布局抖动(layout thrashing)

  • 使用 FastDom 批量对 DOM 的读写操作(eg:动态修改图片宽度)

css 性能让 javascript 变慢?

  • eg: js 写了个死循环, 页面突然卡死,chome 无响应
  • css 核心会影响页面样式的展示,
  • 频繁触发重绘回流,会导致 UI 频繁渲染,最终导致 js 变慢
  • 一个线程=>JavaScript 解析
  • 一个线程=>UI 渲染

什么是重绘与回流

回流必将引起重绘,而重绘不一定会引起回流

回流

  • 当 render tree 中的一部分(或全部) 因为元素的规模尺寸, 布局, 隐藏等改变而需要重新构建。这就成为回流(reflow)
  • 当页面布局和几何属性改变时就需要回流

影响回流的操作

  • 添加/删除元素
  • display:none
  • 移动元素位置
  • 操作 styles
  • offsetLeft,scrollTop, clientWidth
  • 修改浏览器大小,字体大小

重绘

  • 当 render tree 中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如 background-color。则就叫称为重绘。

避免重绘回流的两种方法

1.避免使用触发重绘, 回流的 CSS 属性

  • ①触发页面重布局的属性(回流)
    • 盒子模型相关属性会触发重布局
    • 定位属性及浮动也会重发重布局
    • 改变节点内部文字结构也会触发重布局

  • ②只触发重绘的属性

2.将重绘, 回流的影响范围限制在单独的图层之内

  • 频繁重绘回流的 DOM 元素单独作为一个独立图层,那么这个 DOM 元素和重绘和回流的影响只会在这个图层中

如何将 DOM 元素变成新的独立图层?

Chrome 创建图层的条件

  • 1.3D 或透视变换 CSS 属性(perspective transform)
  • 2.使用加速视频解码的<video 节点
  • 3.拥有 3D(WebGL)上下文或加速的 2D 上下文的<canvas 节点
  • 4.混合插件(如 Flash)
  • 5.对自己的 opacity 做 CSS 动画或使用一个动画 webkit 变换的元素
  • 6.拥有加速 CSS 过滤器的元素
  • 7.元素有一个包含复合层的后代节点(一个元素拥有一个子元素,该子元素在自己的层里)
  • 8.元素有一个 z-index 较低且包含一个复合层的兄弟元素(换句话说就是该元素在复合层上面渲染)

实战优化点总结

  • 1.用 translate 替代 top 改变 ( top 会触发回流但是 translate 不会 )
  • 2.用 opacity 替代 visibility (减少重绘)
  • 3.不要一条一条地修改 DOM 的样式, 预先定义好 class(样式), 然后修改 DOM 的 className
  • 4.把 DOM 离线后修改,比如:先把 DOM 给 display:none (有一次 Reflow),然后你修改 100 次,然后再把它显示出来
  • 5.不要把DOM 结点的属性值放在一个循环里当成循环里的变量 ( offsetHeight offsetWidth )
  • 6.不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局 ( 尽量使用 div )
  • 7.动画实现的速度的选择 ( 合适动画时间 )
  • 8.对于动画新建图层
  • 9.启用 GPU 硬件加速

1.用 translate 替代 top 改变 2.用 opacity 替代 visibility

  • opacity: 没有 paint 的过程, 因为 paint 的过程是生成图层,但是 opacity 实际上是直接改变的图层的 alpha 通道, 所以不涉及图层的重绘
<!DOCTYPE html>
<html lang="en">
<head>
    <title>1.用 translate 替代 top 改变 2.用 opacity 替代 visibility</title>
    <meta charset="UTF-8"></meta>
    <style>
        #rect {
            width: 100px;
            height: 100px;
            background-color: blue;
            /*1.*/
            /*position: relative;*/
            /*top: 0;*/

            /*transform: translateY(0);*/

            /*2*/
            opacity: 1;
            transform: translateZ(0);
        }
    </style>
</head>
<body>
<div id="rect"></div>
<script>
    setTimeout(() => {
        // document.getElementById('rect').style.transform = 'translateY(100px)'
        // 2
        document.getElementById('rect').style.opacity = '0'
    }, 2000)
</script>
</body>
</html>

3.单独 class 样式

<!DOCTYPE html>
<html lang="en">
<head>
    <title>3.不要一条一条地修改 DOM 的样式, 预先定义好 class, 然后修改 DOM 的 className</title>
    <meta charset="UTF-8"></meta>
    <style>
        #rect {
            position: relative;
            width: 100px;
            height: 100px;
            background-color: blue;
            opacity: 1;
        }
        #rect.action{
            /*#rect.action 提高样式优先级*/
            width: 200px;
            height: 300px;
            left: 300px;
            top: 200px;
        }
    </style>
</head>
<body>
<div id="rect"></div>
<script>
    setTimeout(() => {
        document.getElementById('rect').className = 'action'
    }, 2000)
</script>
</body>
</html>

5.不要把 DOM 结点的属性值放在一个循环里当成循环里的变量

<script>
    var doms=[]//通过选择器选择出了一个 dom 元素的数组
    var domsTop=[]
    var clientHeight=document.body.clientHeight//获取可视区域的高度
    //去根据当前页面的可视区域的高度,去计算这个 dom 元素的位置
    for(var i=0;i<doms.length;i++){
        // domsTop.push(document.body.clientHeight+i*100)//浏览器的布局缓存被频繁刷新,导致每次访问 DOM 属性时都需要重新计算,从而影响性能
        domsTop.push(clientHeight+i*100)
    }
</script>

7.动画实现的速度的选择

<!DOCTYPE html>
<html lang="en">
<head>
    <title>7.动画实现的速度的选择  </title>
    <meta charset="UTF-8"></meta>
    <style>
        #rect {
            position: relative;
            top: 0;
            left: 0;
            width: 100px;
            height: 100px;
            background-color: blue;
            opacity: 1;
        }
    </style>
</head>
<body>
<div id="rect"></div>
<script>
    var distance = 0;
    setInterval(()=>{//平滑度高, 流畅, 但是耗性能高,回流重绘次数多
        distance ++;
        document.getElementById('rect').style.top=distance+'px';
        document.getElementById('rect').style.left=distance+'px';
    },100)
</script>
</body>
</html>

8will-change 是一个 CSS 属性,

  • 用于告诉浏览器某个元素在未来可能会发生哪些变化。这可以帮助浏览器优化渲染性能,提前做一些准备工作,从而提高性能。

will-change: transform; 和 transform: translateZ(0);

这段 CSS 代码用于优化动画性能:

  • will-change: transform:提前告知浏览器元素将要发生变换,让浏览器预先进行性能优化

9.启用 GPU 硬件加速

  • transform: translateZ(0):创建硬件加速上下文,将元素提升到新的合成层,避免重绘和回流,提高动画流畅度
    两者结合使用可以显著提升 CSS 动画和变换的渲染性能。

  • transform: translate3d(0,0,0): 不能滥用,
    创建一个 3D 变换上下文
    强制浏览器将该元素提升到独立的合成层
    触发 GPU 硬件加速,使动画更加流畅

浏览器存储

cookies

  • 因为 HTTP 请求无状态,所以需要 cookie 去维持客户端状态
  • cookie 的生成方式:
    • http response header 中的 set-cookie
    • js 中可以通过 document.cookie 可以读写 cookie
  • 两种作用
    • ①用于浏览器端和服务器端的交互
    • ②客户端自身数据的存储
  • 过期时间:expire

cookie 存储的限制

  • 仅仅作为浏览器存储,大小 4KB 左右
  • 需要设置过期时间 expire
  • cookie 存储数据能力被 localstorage 替代
  • cookie 不支持 js 读写----httponly
  • cookie 中在相关域名下面--cdn 的流量损耗
    • 解决方法: cdn 的域名 和主站的域名要分开

localStorage

  • HTML5 设计出来专门用于浏览器存储的
  • 大小为 5M 左右
  • 仅在客户端使用,不和服务端进行通信
  • 接口封装较好
  • 浏览器本地缓存方案

sessionStorage

  • 会话级别的浏览器存储
  • 大小为 5M 左右
  • 仅在客户端使用,不和服务端进行通信
  • 接口封装较好
  • 对于表单信息的维护 (多页面切换时)

IndexedDB

  • IndexedDB 是一种低级 API,用于客户端存储大量结构化数据。该 API 使用索引来实现对该数据的高性能搜索。虽然 WebStorage 对于存储较少量的数据很有用,但对于存储更大量的结构化数据来说,这种方法不太有用。IndexedDB 提供了一个解决方案。
  • 为应用创建离线版本

PWA

  • 可靠:在没有网络的环境中也能提供基本的页面访问,而不会出现“未连接到互联网”的页面
  • 快速:针对网页渲染及网络数据访问有较好优化
  • 融入(Engaging):应用可以被增加到手机桌面,并且和普通应用一样有全屏、推送等特性。
  • lighthouse

Service Worker

  • 是一个脚本,浏览器独立于当前网页,将其在后台运行,为实现一些不依赖页面或者用户交互的特性打开了一扇大门。在未来这些特性将包括推送消息,背景后台同步,geofencing(地理围栏定位),但它将推出的第一个首要特性,就是拦截处理网络请求的能力,包括以编程方式来管理被缓存的响应。
  • 可以实现:
    • 1.使用缓存完成当前 PWA 离线应用的能力
    • 2.和主页面的通信,(页面中有非常大的数据处理,非常大的逻辑运算---不依赖页面或者用户交互的特性,可以在后台运行,不阻碍主线程的执行)

应用:

  • 1.使用拦截和处理网络请求的能力,去实现一个离线应用
  • 2.使用 Service Worker 在后台运行同时能和页面通信的能力, 去实现大规模后台数据的处理

Cache-Control

是 HTTP 协议中的一个响应头/请求头,用来控制缓存行为。它可以决定浏览器、CDN、代理服务器等中间缓存的存储方式和有效时间。

  • max-age:
    • 指定资源在 缓存中的最大存活时间(以秒为单位)。
    • 在 max-age 有效期内,浏览器或代理会直接用缓存,不会去请求服务器。
    • 1h: 3600s, 1 天 86400s,
    • 0s 等价于 Expires: 0 [表示客户端只接受最新资源,不要用缓存。]
  • s-maxage
    仅共享缓存(CDN、代理)优先级比 max-age 高
  • no-cache
    不是“不缓存”,而是 可以缓存,但用之前必须向服务器确认是否过期。
    它依赖 协商缓存(ETag / Last-Modified)。
  • no-store
    完全不缓存,连磁盘/内存都不保存(最严格,常用于敏感信息)
  • public
    • 资源可以被 任何缓存 存储:浏览器缓存、代理缓存(CDN、代理服务器、反向代理等)。
    • 默认情况下,大部分静态资源(图片、CSS、JS)都是 public。
  • private
    • 资源只能被 单个用户的浏览器缓存,共享缓存(CDN/代理)不能存。
    • 适合和用户身份强相关的数据。
    • 常用于 用户个人主页、购物车、账户信息 等。

Expires

  • 是 HTTP/1.0 的缓存控制头,用于指定一个 绝对的过期时间(GMT 时间)。
  • 在过期时间之前,浏览器或代理缓存可以直接从浏览器缓存取数据,不用去服务器请求。

last-modified 缺点

  • 某些服务端不能获取精确的修改时间
  • 文件修改时间改了, 但文件内容却没有变


总结缓存策略场景速查表

实战

喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
已稳定运行:3年255天2小时11分