HTTP 请求过程中潜在的性能优化点
- dns是否可通过缓存减小dns查询时间?
- 网络请求的过程在最近的网络环境?
- 相同的静态资源是否可缓存?
- 能否减小请求http请求大小?
- 减小http请求
- 服务端渲染
深入理解http请求的过程是前端性能优化的核心
一 资源压缩与合并
即减小HTTP请求数量与大小 google首页案例学习
- HTML压缩: 去除空格/制表符/换行符等
- CSS压缩: 无效代码去除 / 语义合并
- JS压缩与混乱: 无效字符删除 / 剔除注释 / 代码语义缩减和优化 / 代码保护
- 文件合并
文件合并存在的问题:首屏渲染问题 / 缓存大面积失效问题 解决问题:公共库合并 / 不同页面的合并 / 见机行事,随机应变 - 开启gzip
二 图片相关
有损压缩
根据色彩丰富程度选择格式
- png8 256色 + alpha
- png24 2^24色
- png32 2^24色 + alpha
业务场景
-
jpg有损压缩、压缩率高、不支持透明
-
png支持透明,浏览器兼容好
-
webp压缩程度更好,在ios webvier有兼容问题
-
svg适量图,代码内嵌,相对小,图片样式相对简单场景
-
jpg - 不需要透明场景
-
png - 需要透明场景
-
webp - 安卓
-
svg - 图片样式相对简单如icon-font logo
01 CSS雪碧图
减少网站HTTP请求数量(postCSS可自动化制作雪碧图)
02 Image inline
将图片内容内嵌到html中,减小HTTP请求数量
03 使用适量图
使用iconfont解决icon问题
04 安卓下使用webp
转化效果优秀,稳定统一
三 CSS和JS的装载与执行
- 顺序执行、并发加载(并发受域名限制)
- 资源是否会阻塞
- 依赖关系
- 引入方式
词法分析是从上到下的,顺序执行
-
css head中阻塞页面的渲染
-
css不阻塞外部js的加载,但是会阻塞js的执行
-
直接引入JS会阻塞页面渲染
-
JS不阻塞资源的加载
-
JS顺序执行,阻塞后续JS逻辑的执行
四 懒加载与预加载
01 预加载
资源在使用时能从缓存中加载,提升用户体验 例1:在网络闲置时偷偷加载后面的资源 异步import 例2:播放视频音乐时边下边播
02 懒加载
减少无效资源加载 且 并发时会阻塞JS加载 ==> 需要时在加载
例1:动态import异步加载资源
例2:图片进入可视区域时在动态把src地址加入src
<img src="" lazyload="true" data-original="http://img.com/001.jpg" width="100" height="100"/>
<img src="" lazyload="true" data-original="http://img.com/002.jpg" width="100" height="100"/>
图片的src是空的,真实的url在data-original中
var viewHeight = document.documentElement.clientHeight // 可视区域高度
function lazyload(){
var eles = document.querySelectorAll('img[data-original][lazyload]')
Array.prototype.forEach.call(eles, function(item, index){
var rect
if(item.dataset.original === ''){
return
}
rect = item.getBoundingClientRect()
if(rect.bottom >= 0 && rect.top < viewHeight){
!function(){
var img = new Image()
img.src = item.dataset.url
img.onload = function(){
item.src = img.src
}
item.removeAttribute('data-original')
item.removeAttribute('lazyload')
}()
}
})
}
lazyload()
document.addEventListener('scroll', lazyload)
zepto的zepto.lazyload也实现了lazyload
五 重绘与回流
CSS性能让JS变慢? 一个线程JS解析 一个线程UI渲染 频繁触发重绘与回流,会导致UI频繁渲染,最终导致JS变慢
01 回流
- 当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)
- 当页面布局和几何属性改变时就需要回流
适当减少CSS触发回流
02 重绘
当render tree中的一些元素需要更新属性,而这些属性只影响元素外观,风格,而不影响布局的,比如background-color。则称为重绘
回流必将引起重绘,而重绘不一定引起回流
03 避免重绘回流的两种方法
会触发页面重布局的属性
- 盒模型相关属性
- 定位属性及浮动
- 改变节点内部文字结构
| box | 定位 | 文本 |
|---|---|---|
| width | display | text-align |
| height | top | font-weight |
| padding | bottom | font-family |
| margin | bottom | line-height |
| border-width | left | vertival-align |
| border | right | white-space |
| min-height | position | font-size |
| overflow-y | float | |
| overflow | clear |
只会触发重绘的属性
| text & box | background | outline |
|---|---|---|
| color | background | outline-color |
| visibility | background-image | outline |
| text-decoration | background-image | outline-style |
| border-style | background-positon | outline-width |
| border-radius | background-repeat | |
| box-shadow | background-size |
04 新建DOM的过程
- 获取DOM后分割为多个图层
- 对每个图层的节点计算样式结果(Recalculate style--样式重计算)
- 为每个节点生成图形和位置(Layout--回流和重布局)
- 将每个节点绘制填充到图层位图中(Paint Setup和Paint--重绘)
- 图层作为纹理上传转GPU
- 符合多个图层到页面上生成最终屏幕图像(Composite Laters -- 图层重组)
** 将频繁重绘回流的DOM元素单独作为一个独立图层,那么这个DOM元素的重绘和回流的影响只会在这个图层中。 **
05 如何将DOM元素变成新的独立图层?
Chrome创建图层的条件
- 3D或透视变换CSS属性(perspective transform) (transform:translateZ(0) 将普通DOM变成图层)
- 使用加速视频解码的<video>节点
- 拥有3D(webGL)上下文或加速的2D上下文的<canvas>节点
- 混合插件 如flash
- 对自己的opacity做css动画或使用一个动画webkit变换的元素
- 拥有加速CSS过滤器的元素
- 元素有一个包含复合层的后代节点(一个元素拥有一个子元素,该子元素在自己的层里)
- 元素有一个z-index较低且包含一个复合层的兄弟元素(该元素在复合层上渲染)
例:gif会频繁触发重绘,但又不在独立图层中。
06 结论
- 避免使用触发重绘、回流的CSS属性
- 将重绘,回流的影响范围限制在单独的图层之内
07 chrome工具
chrome > performance工具会详细展示从请求到渲染过程
chrome > Layers 查看渲染图层
chrome > Rendering > Paint flashing 会将重绘区域标示绿块
图层layers会非常消耗性能,不能“烂”用
08 最佳实践
- 用translate替代top改变 (top会触发layout)
- 用opacity替代visibility(visibility会触发重绘)
- 不要一条一条修改DOM的样式,预先定义好class,然后修改DOM的className
- 把DOM离线后修改(如:先把DOM给display:none(有一个reflow),然后修改1000次,最后显示出来)
- 不要把DOM结点的属性值放在一个循环里当成循环里的变量(offsetHeight / offsetWidth 获取此类属性即会触发回流)
- 不要使用table布局,可能很小一个改动会造成整个table重新布局
- 动画实现的速度的合理选择
- 对于动画新建图层
- 启用GPU硬件加速
- 为gif新建图层
六 浏览器存储
pwa / service worker
01 cookie
-
因为HTTP请求无状态,所以需要cookie去维持客户端状态
-
用于客户端与服务端的交互: http response header 中的 set-cookie,由服务端生成,客户端存储
-
用于客户端自身数据存储: JS中可通过document.cookie读写cookie,需要设置过期时间expire
-
cookie作为客户端存储,数据限制大小4KB左右(已被localstorage替代)
-
httponly:禁用JS读写
cookie在相关域名下面-cdn的流量损耗 解决方法:cdn的域名和主站域名要分开
Chrome ==> Application ==> Cookies 查看
02 LocalStorage
- HTML5设计出来专门用于浏览器存储的
- 大小为5M左右
- 仅在客户端使用,不和服务端进行通信
- 接口封装较好
- 浏览器本地缓存方案
Chrome ==> Application ==> Local Storage 查看
var testJsContent = localStorage.getItem('test')
if(testJsContent){
eval(testJsContent)
}else{
axios.get('/api/test.js').then((data)=>{
localStorage.setItem('test', 'data')
})
}
03 SessionStorage
- 会话级别的浏览器存储(标签内存储,关闭标签后清空)
- 大小为5M左右
- 仅在客户端使用,不和服务端进行通信
- 接口封装较好
- 对于表单信息的维护
Chrome ==> Application ==> Session Storage 查看
04 IndexedDB
- 是一种低级API,用于客户端存储大量结构化数据。该API使用索引来实现对该数据的高性能搜索。虽然webStorage对于存储较少量的数据很有用,但对于存储更大量的结构化数据来说,这种方法不太有用。IndexedDB提供了一个解决方案。
- 为应用创建离线版本
05 Service Workers
客户端JS为单线程,当我们需要大消耗计算时会障碍JS与UI渲染 解决:由service workers发送到后台计算,计算完成后返回客户端渲染
Service Worker 是一个脚本,浏览器独立于当前页面,将其在后台运行,为实现一些不依赖页面或用户交互的特性打开了一扇大门。在未来这些特性将包括投送消息,背景后台同步,geofencing(地理围栏定位),但它将推出的第一个首要特性,就是拦截和处理网络请求的能力,包括以编程方式来管理被缓存的响应。
应用点:
- 使用拦截和处理网络请求的能力,去实现一个离线应用
- 使用service worker在后台运行同时能和页面通信的能力,实现大规模后台数据的处理
生命周期
06 PWA
progressive web apps 是一种web app新模型,并不是具体指某一种前沿的技术或某一个单一的知识点,这是一个渐进式的web app,是通过一系列新的web特性,配合优秀的UI交互设计,逐步的增强web app 的用户体验。
- 可靠:在无网络环境或弱网环境提供基本的页面访问,而不会出现"未连接到互联网"的页面
- 快速:针对网页渲染及网络数据访问有较好优化
- 融入Engaging:应用可被增加到手机桌面,并且和普通应用一样有全屏、投送等特性
- chrome 插件 lighthouse 用于检测PWA指标
七 缓存
httpheader
Cache-Control
- max-age : 缓存最大有效时间数,比expires过期时间优秀级更高
- s-maxage : 指定public缓存时间
- private : 客户端私有缓存
- public : CDN缓存设备中的缓存
- no-cache : 去服务端发送请求判断是否使用浏览器缓存
- no-store : 完全不使用缓存
Expires
- 缓存过期时间,用来指定资源到期时间,是服务端的具体的时间点。
- 告诉浏览器在过期时间前浏览器可直接从浏览器缓存存取数据,而无需再次请求
问题是当服务端数据变更,客户端无法感知 解决:last-modified / if-modified-since
- 基于客户端和服务端协商的缓存机制
- last-modified - response header
- if-modified-since - request header
- 需要与cache-control共同使用
客户端发送请求 => 服务端返回数据带上last-modified => 客户端保存last-modified 客户端之后每次请求都会带上if-modified-since => 服务端确认modified是否过期 (未过期返回304 / 过期返回新的last-modified及200)
last-modified 缺点
- 某些服务端不能获取精确的修改时间
- 文件修改时间改了,但文件内容没有变
Etag/if-None-Match
- 文件内容的hash值
- etag - response header
- if-none-match - request header
- 需要与cache-control共同使用
无变动返回304 、 有变动返回200
分级缓存策略
八 前端性能在服务端的优化
VUE React 类框架 有下载框架代码 => 执行JS => 渲染页面的性能损耗
多层次的优化方案
- 构建层模板编译
- 数据无关的prerender的方式
- SSR 服务端渲染
九 前端性能优化祖宗 - 雅虎军规
- 减少HTTP请求
- 减少DNS查询
- 避免跳转 301 302
- 可缓存的AJAX
- 推迟加载内容
- 预加载
- 减少DOM元素数量
- 根据域名划分页面内容
- 使iframe数量最小
- 不要出现404
- 使用内容分发网络
- 为文件头指定Expires或Cache-Control
- Gzip压缩文件内容
- 配置ETag
- 尽早刷新输出缓冲
- 使用GET来完厂AJAX请求
- 把样式表置于顶部
- 避免CSS表达式
- 使用外表JS与CSS
- 削减JS和CSS
- 用<link>代替@import
- 避免滤镜
- 把脚本置于页面底部
- 剔除重复脚本
- 减少DOM访问
- 开发智能事件处理程序
- 减小Cookie体积
- 对页面内容使用无cookie域名
- 优化图像
- 优化CSS Spirite
- 不要在HTML中缩放图像
- favicon.ico要小且可缓存
- 保持单个内容小于25k
- 打包组件成复合文本