目录
1. 资源合并与压缩
http请求过程及潜在的性能优化点
- 减少http请求的数量
- 减少请求的大小
HTML压缩
HTML代码压缩就是压缩这些在文本文件中有意义,但是在HTML中不显示的字符,包括空格、制表符、换行符等,还有一些其他意义的字符,如HTML注释也可以被压缩
如何进行HTML压缩
- 使用在线网站进行压缩(一般是构建工具压缩)
- node.js提供html-minifier工具
- 后端模板引擎渲染压缩
CSS 压缩
- 无效代码删除,如注释和无效字符
- css语义合并
css 压缩方式
- 使用在线网站
- 使用html-minifier 对html中的css进行压缩
- 使用clean-css 对css压缩
js压缩与混乱
无效字符的删除 空格、注释、回车等
剔除注释
代码语义的缩减与优化,如变量名缩短等
代码保护
js压缩方式
- 在线网站
- 使用html-minifier对html中的js进行压缩
- 使用uglifyjs2对js进行压缩
不合并文件可能存在的问题
- 文件与文件之间插入的上行请求,增加了N-1个网络延迟
- 受丢包问题严重
- 经过代理服务器时可能会被断开
文件合并
公共库的合并
不同页面的合并:不同页面的js要单独打包
文件合并方法
使用在线网站
构建阶段,使用nodejs进行文件合并
2. 图片相关的优化
png8
/png24
/png32
之间的区别
png8 256色 + 支持透明
png24 2^24 + 不支持透明
png32 2^24 + 支持透明
不同格式图片的特点
jpg有损压缩,压缩率高,不支持透明
png支持透明,浏览器兼容性好
webp压缩程度更好,在iOS webview中有兼容性问题
svg矢量图,代码内嵌,相对较小,图片样式相对简单
不同格式图片的使用场景
jpg :大部分不需要透明图片的场景
png :大部分需要透明图片的场景
webp:andriod全部(解码速率和压缩率高于jpg和png,但iOS Safari还没支持)
svg:图片相对简单的场景
图片压缩的几种情况
- 针对真实图片情况,舍弃一些相对无关紧要的色彩信息
- CSS雪碧图:把网站中用到的一些图片整合到一张单独的图片中
优点:减少http请求的数量(通过background-position定位所需图片)
缺点:整合图片较大,加载速度较慢
- Image-inline:将图片的内容嵌到HTML中(减少http请求)
base64信息 ,减少网站的http请求,如果图片比较小比较多,时间损耗主要在请求的骨干网路
- 使用svg矢量图:
使用svg进行矢量图的绘制
使用icon-font 解决icon问题
- 在andriod下使用webp
webp优势在于压缩率高,能带来更小的图片体积
同时具备有损和无损的压缩模式,在JPEG和png上的转化效果都很好
3. css和js的装载与执行
网站在浏览器端的渲染过程
HTML渲染过程中的一些特点
- 顺序执行,并发加载
词法分析:从上而下依次解析。通过HTML生成token对象(当前节点的所有子节点生成后,才会通过next token获取当前节点的兄弟节点),最终生成token tree
并发加载:资源请求是并发请求的
并发上限:
浏览器支持并发请求,不同浏览器所支持的并发数量不同(以域名划分),以Chrome为例,并发上限为6个
优化:把CDN资源分布在多个域名下
- 是否阻塞
a . css阻塞
--
css
在head
中通过link
引入会阻塞页面的渲染css在head中通过link引入,整个页面的渲染都会等待head中的css加载并生成css树,最终和dom整合生成RenderTree之后才会进行渲染
-- css 不阻塞js的加载,但会阻塞js的执行
-- css 不阻塞外部脚本的加载
b . js 阻塞
-- 直接通过<script src="">引入会阻塞后面节点的渲染
--- html parse 会动态修改文档结构
--- async defer 属性
---- defer 属性 延迟执行引入的js脚本,即脚本的加载是不会导致解析的停止,等到document全部解析完 毕,defer-script也加载完毕,再执行代码,然后触发documentloaded
---- async 属性 异步执行引入的js脚本,与defer的区别是async会在加载完成后就执行,但不会阻塞解析和渲染。但会阻塞load事,件,所以async-script可能会在DOMcontentloaded触发前或后执行,但一定会在load事件前触发。
4. 懒加载与预加载
懒加载
- 图片进入可视区域后请求图片资源
- 对于电商等图片很多,页面很长的业务场景适用
- 减少无效资源的加载
- 并发加载的资源过多会阻塞js的加载,影响网站的正常使用
注: img src 被设置之后,webkit解析到后才会去请求这个资源。所以图片到达可视区之后,src真正的图片才会被设置进去,没有达到可视区前,没有真正的src,类似于一个占位符
应用场景:电商图片
懒加载实现:原生js
先将img标签中的src链接设为同一张图片(空白图片),将其真正的图片地址存储在img标签的自定义属性中(比如data-src)中。当js监听到该图片元素进入可视区域时,将自定义属性中的地址存储到src属性中去,达到懒加载的效果
<div class="image-list">
<img src="" class="image-item" lazyload = "true" data-original = "xxxxx">
<img src="" class="image-item" lazyload = "true" data-original = "xxxxx">
<img src="" class="image-item" lazyload = "true" data-original = "xxxxx">
<img src="" class="image-item" lazyload = "true" data-original = "xxxxx">
<img src="" class="image-item" lazyload = "true" data-original = "xxxxx">
<img src="" class="image-item" lazyload = "true" data-original = "xxxxx">
</div>
var viewHeight = document.docunmentElement.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()
documment.addEventListener('scroll',lazyload);
预加载
- 图片等静态资源在使用前的提前请求
- 资源使用到的时候能从缓存中加载,提升用户体验
- 页面展示的依赖关系维护
应用场景:抽奖
预加载实现:
a.直接请求下来
<img src="https://user-gold-cdn.xitu.io/2019/2/21/1690d1b216cbfa18" style="display: none"/>
b. image对象
var image = new Image();
image.src = "www.pic26.com/dafdafd/safdas.jpg";
c. xmlhttprequest
var xmlhttprequest = new XMLHttpRequest();
xmlhttprequest.onreadystatechange = callback;
xmlhttprequest.onprogress = progressCallback;
xmlhttprequest.open("GET","http:www.xxx.com",true);
xmlhttprequest.send();
function callback(){
if(xmlhttprequest.readyState == 4 && xmlhttprequest.status == 200){
var responseText = xmlhttprequest.responseText;
}else{
console.log("Request was unsuccessful:" + xmlhttprequest.status);
}
}
function progressCallback(){
e = e || event;
if(e.lengthComputable){
console.log("Received"+e.loaded+"of"+e.total+"bytes")
}
}
5. 重绘与回流
CSS性能让JavaScript变慢?
将css通过head引入,加载css时会阻塞整个页面的渲染,同样执行js代码的时候也会阻塞,如,js死循环
一个线程 ==》 JavaScript解析
一个线程 ==》 UI渲染
这两个线程互斥,当UI渲染的时候,js执行终止,当js执行时,UI线程被冻结,所以css性能会让JavaScript变慢
什么是回流 (reflow)
当render-tree中的部分或全部因为元素尺寸、布局、隐藏等改变而需要重新构建,就构成回流
当页面布局和几何属性改变时,就需要回流
什么是重绘(repaint)
当render-tree中的元素需要更新新的属性,而这些属性只会影响到元素的外观、风格,而不会影响到布局,需要重绘
二者关系
回流必将引起重绘,而重绘不一定引起回流
触发页面布局的一些css属性
- 盒子模型相关属性会触发重布局
width height padding margin display border-width border min-height
- 定位属性及浮动也会触发重布局
top bottom left right position float clear
- 改变节点内部文字结构也会触发回流
text-align overflow font-weight font-family line-height vertical-align white-space font-size
- 只触发重绘不触发回流的元素
color
border-style/border-radius
visibility
text-decoration
background/backgroud-image/background-position/backgroud-repeat/background-size
outline
box-shadow
chrome创建图层的条件
将频繁重绘回流的DOM元素单独作为一个独立的图层,那么这个DOM 元素的重绘和回流的影响只会在这个图层中
- 3D或透视变换
- CSS属性使用加速视频解码的<video>元素
- 拥有3D(webGL)上下文或加速
- 2D上下文的<canvas>元素
- 进行opacity、transform动画的元素拥有加速
- 元素有一个z-index较低且包含一个复合层的兄弟元素
总结:
- 尽量避免使用触发回流、重绘的CSS属性
- 将重绘、回流的影响范围限制在单独的图层之内
- 图层合成过程中消耗很大的页面性能,这是要均衡考虑
实现优化点总结
- 用translate替代top属性
- 用opacity代替visibility
- opacity不会触发回流重绘,只是改变图层的alpha值,但是需将图片独立出一个图层
- visibility会触发重绘
- 不要一条一条修改DOM样式,预先定义好class,然后修改dom的className
- 把DOM离线后修改,如DOM给display:none,然后修改100次,然后再把他显示出来
- 不要把DOM节点的属性值放在循环里当成循环变量
- 不要使用table布局,可能很小的改动都会造成整个table的重新布局
- 动画实现的速度选择
- 选择合适的动画速度
- 根据performance量化性能优化
- 对于动画新建图层
- 启用GPU硬件加速,GPU加速意味着数据需要从cpu总总线到GPU传输,需要考虑传输损耗
6. 浏览器存储
cookies
因为http请求无状态,所以需要cookie去维持客户端状态
cookie的生成方式:
http --> response header --> set-cookie
js 中通过document.cookie可以读写cookie
cookie的用处:
-- 用于浏览器端和服务器端的交互(用户状态)
-- 客户端自身数据的存储
expire:过去时间
cookie的限制:
-- 作为浏览器存储,大小在4KB左右
-- 需要设置过期时间 expire
重要属性:httponly 不支持js读写(防止收到模拟请求攻击)
不太作为存储方案而是用于维护客户关系
优化点: cookie在相关域名下面
-- cdn 的流量消耗
解决方案:cdn的域名和主域名要分开
localStorage
HTML5设计出来专门用来浏览器存储
大小在5M左右
仅在客户端使用,不和服务期间通信
接口封装较好
浏览器本地缓存方案
sessionStorage
会话级别的浏览器存储
大小在5M左右
仅在客户端使用,不和服务器通信
接口封装好
对于表单信息的维护
indexDB
indexDB 是一种低级API,用于客户端存储大量结构化数据。该API使用索引来实现对该数据的高性能搜索。
PWA
PWA (progressive web apps)是一种web app新模型,并不是具体指某种前言技术,是一个渐进式的web app,通过一系列新的web特性,配合优秀的UI交互设计,逐步增强web app的用户体验。
检测是不是PWA
-- 当前手机在弱网环境下能不能加载出来
-- 离线环境下能不能加载出来
特点:
-- 可靠:在没有网络情况下也能提供基本的页面访问
-- 快速:针对页面渲染和网络数据访问有较好的优化
-- 融入:应用可以被增加到手机桌面,并且和普通应用一样有全屏、推送等特性
service worker
service worker是一个脚本,浏览器独立于当前页面,将其在后台运行,有一首要特性,就是拦截和处理网络请求的能力,包括以编程方式来管理被缓存的响应。
service worker网络拦截能力,存储catch storage ,实现离线应用
7. 缓存
强缓存:不会向服务器发送请求,直接从缓存中读取资源,在chrome控制台的Network选项中可以看到该请求返回200的状态码,并且Size显示from disk cache或from memory cache。强缓存可以通过设置两种 HTTP Header 实现:Expires 和 Cache-Control。
协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,主要有以下两种情况:
-
协商缓存生效,返回304和Not Modified
-
协商缓存失效,返回200和请求结果
缓存机制:
强制缓存优先于协商缓存进行,若强制缓存(Expires和Cache-Control)生效则直接使用缓存,若不生效则进行协商缓存(Last-Modified / If-Modified-Since和Etag / If-None-Match),协商缓存由服务器决定是否使用缓存,若协商缓存失效,那么代表该请求的缓存失效,返回200,重新返回资源和缓存标识,再存入浏览器缓存中;生效则返回304,继续使用缓存。
httpheader
可缓存性:
public :表明响应可以被任何对象缓存
private:响应只能被单个用户缓存,不能作为共享缓存(代理服务器不能缓存)
no-catch:强制所有缓存了该响应的缓存用户,在使用已存储的缓存数据之前,发送带验证器的请求到原始服务器
only-if-cached:表明如果缓存存在,只是用缓存,无论原始服务器的数据是否有更新
到期:
max-age=<seconds> : 设置缓存存储的最大周期,超过这个时间缓存会被认为过期,与expire相反,时间是相对于请求的时间。max-age优先级高于expires,当有max-age时expires可能会被忽略。
s-maxage=<seconds>:覆盖max-age或expire,但是仅用于public
last-modified 和If-Modified-since
基于客户端和服务端协商的缓存机制
last-modified --> response header
if-modified-since --> request header
需要与catch-control共同使用
Etag 和 If-none-match
文件内容的hash值
etag --> response header
if-none-match --> request header
需要和catch-control共同使用
好处:比if-modified-since更加准确,优先级比last-modified更高
流程图:
看到一篇很好的关于浏览器缓存的文章:https://juejin.cn/post/6844904053223358471#heading-6