前端性能优化点
- dns是否可以通过缓存减少dns查询时间
- 网络请求的过程走最近的网络环境?
- 相同的静态资源是否可以缓存
- 能否减少http请求大小?
- 减少http请求
- 服务端渲染
资源的合并与压缩
- 实际是就是 减少http请求数量(合并)
- 减少请求资源的大小(压缩)
html压缩
- 使用再线网站进行压缩
- nodejs提供htmn-minifier工具
- 后端模板引擎渲染压缩
css压缩
- 无效代码删除
- css语义合并
js压缩与混乱
- 无效字符的删除
- 剔除注释
- 代码语义的压缩和优化
- 代码保护
文件合并
- 使用工具fis3
图片的解析过程
png8 ———— 256色 + 支持透明度
png24 ———— 2^24色 + 不支持透明度
png32 ———— 2^24色 + 支持透明度
不同格式图片常用的业务场景
- jpg有损压缩,压缩率高,不支持透明 [不需要透明图片的业务场景]
- png指出透明,浏览器兼容号 [大部分需要透明图片的业务场景]
- webp压缩程度最好,在ios webview有兼容性问题 [安卓全部]
- svg矢量图,代码内嵌,相对较小,图片样式相对简单的场景 [图片样式相对简单的业务场景]
进行图片压缩,即针对真实图片情况,舍弃一些相对无关紧要的色彩信息
css雪碧图
把你的网站上用到的一些图片整合到一张单独的图片中
优点:减少你的网站的http请求数量
缺点: 整合图片比较大时,一次加载比较慢
制作雪碧图的网站:雪碧牛
Image inline
将图片的内容嵌套到html当中
使用矢量图
使用svg进行矢量图的绘制
使用iconfont解决icon问题
获取矢量图的网站:阿里图库
在安卓下使用webp
HTML渲染过程的一些特点
顺序执行,并发加载
(css的link的引用),而且并发是根据浏览器的限制的
分析的顺序和并发的加载
词法分析
并发加载
并发上限
是否阻塞
css阻塞
- css head 中阻塞页面的渲染
- css阻塞js的执行
- css不阻塞外部脚本的加载
js阻塞
- 直接引入的js阻塞页面的渲染
- js不阻塞资源的加载
- js顺序不执行阻塞后续js逻辑的执行
依赖关系
- 页面渲染依赖于css加载
- js的执行顺序的依赖关系
- js逻辑对于dom节点的依赖关系
引入方式
js引入方式
- 直接引入
- defer(让浏览器知道,这个js文件不要阻塞)
- async
- 异步动态引入js (路由跳到页面再引入这些依赖)
加载和执行的一些优化点
- css样式表置顶
- 用link代替import
因为@import有2个缺陷(旧版本的浏览器中)
不支持并发执行(x)
需要等整个页面加载完成才会执行import(x)
如果import的css存在嵌套import,他的进行是链式进行的,而不会并发请求解决办法:在构建层的时候进行优化(webPack),识别import标签,并且把里面的css直接嵌套到对应的css文件中
优点:
有利于css模块化的维护
- js脚本置地
- 合理使用js的异步加载能力
理解懒加载和预加载的原理
本质:权衡浏览器本身具有的加载资源的能力。(个人觉得其实就是为了提升用户体验,和项目本身的性能)
懒加载
- 图片进入可视区域之后请求图片资源
- 减少无效资源的加载
- 并发加载的资源过多会阻塞js的加载,影响网站正常使用
需要去监听scroll事件,在scroll事件的回调中,去判断我们的懒加载的图片是否进入可视区域
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()
// !function == (function(){})() -> 立即执行函数
if(rect.bottom >= 0 && rect.top < viewHeight){
!function () {
var img = new Image()
img.src = item.dataset.original
img.onload = function() {
item.src = img.src
}
item.removeAttribute('data-original')
item.removeAttribute('lazyload')
}
}
})
}
lazyload() // 最开始的时候要先执行一次
document.addEventListener('scroll',lazyload) // 监听滚动条
使用zepto工具
// 使用工具zepto
$('img[data-original][lazyload]').lazyload()
预加载
- 图片等静态资源在使用之前的提前请求
- 资源使用到时能从缓存中加载,提升用户体验
- 页面展示的依赖关系维护
可以设置display:none;
<image src="http://xxx.jpg" style="display:none;"></image>
使用Image对象,利用js添加(可以更详细的控制过程)
var image = new Image()
image.src = 'http://ssss 2.jpg'
使用XMLHttpRequest对象
var xmlhttprequest = new XMLHttpRequest();
xmlhttprequest.onreadystatechange = callback; // 请求完成后的回调
xmlhttprequest.onprogress = progressCallback; // 请求的过程的回调
xmlhttprequest.open("GET","http://image.baidu.com/mouse.jpg",true)
xmlhttprequest.send()
function callback(){
if(xmlhttprequest.readyState == 4 && xmlhttprequest.status == 200){
var reponseText = xmlhttprequest.responseText
}else{
console.log("Request was unsuccessful: " + xmlhttprequest.status)
}
}
function progressCallback(e){
e = e || event;
if(e.lengthComputable){
console.log("Received " + e.loaded + " of " + e.total + " byest")
}
}
使用PreloadJs 模块
var queue = new createjs.LoadQueue(false);
queue.on("complete", handleComplete, this); // 监听加载完成后执行handleComplete函数
queue.loadManifest([ // 预加载的队列
{id:"myImage", src:"http://xxxxxx 2.jpg"},
{id:"myImage2", src:"http://xxxxxx 3.jpg"}
]);
function handleComplete() {
var image = queue.getResult("myImage");
document.body.appendChild(image); // 操作dom
}
重绘域回流会影响性能
原因: 频繁的出发重绘与回流,会导致ui频繁渲染,最终导致js变慢
解决办法
- 避免使用触发重绘,回流的css属性
- 将频繁重绘回流的DOM元素单独作为一个独立图层,那么这个DOM元素的重绘和回流的影响只会在这个图层中
回流(必定包含重绘)
当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建(当页面布局和几何属性改变时就需要回流),这就称为回流(reflow)
会触发的属性
盒子模型相关属性会触发重布局 | 定位属性及浮动也会触发重布局 | 改变节点内部文字结构也会触发重布局 |
---|---|---|
width | top | text-align |
height | bottom | overflow-y |
padding | left | font-weight |
margin | right | overflow |
display | position | font-family |
border-width | float | line-height |
border | clear | vertival-align |
min-height | white-space | |
font-size |
重绘(不一定包含回流)
render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。 则就叫称为重绘,需要注意的是重绘是以图层为单位的,图层中其中之一需要重绘,则整个图层都需要重绘
只触发重绘的属性 |
---|
color, background, outline-color |
border-style, background-image, outline |
border-radius, background-position, outline-style |
visibility, background-repeat, outline-width |
text-decoration, background-size, box-shadow |
新建DOM的过程
- 获取DOM后分割为多个图层
- 对每个图层的节点计算样式结果(Recalculate style --- 样式重计算)
- 为每个节点生成图形和位置(Layout -- 回流和重布局)
- 将每个节点绘制填充到图层位图中(Paint Setup和Paint -- 重绘 )
- 图层作为纹理上传至GPU
- 符合多个图层到页面上生成最终屏幕图像(Composite Layers图层重组)
Chrome 创建图层的条件
不过需要注意的是图层过多也会影响性能,因为图层合成也会消耗比较多的性能
- 3d或透视变换css属性(perspective transform)
- 使用加速视频解码的<video>节点
- 拥有3d(WebGL)上下文或加速的2D上下文的<canvas>节点
- 混合插件(如Flash)
- 对自己的opacity做css动画或使用一个动画webkit变换的元素
- 拥有加速css过滤器的元素(例如:translate3D)
- 元素有一个包含复合层的后代节点 (一个元素拥有一个子元素,该子元素在自己层里)
- 元素有一个z-index 较低且包含一个复合层的兄弟元素(即该元素在复合层上面渲染 比如:gif)
总结(重绘与回流)的优化点
- 用translate 替代 top 改变
使用top会触发回流,而使用translate则不会触发回流
- 用opacity 替代 visibility
使用opacity替代visibility的时候需要注意的是,必须要让该dom独立称为一个单独的图层(即:添加transfrom:transfromz(0) ),否则浏览器仍然会有回流和重绘
- 不要一条一条的修改 DOM 的样式,预先定义好 class,然后修改DOM的className
虽然直接一条条修改也有可能只执行一次回流,但是这其实利用的是浏览器的缓冲机制完成的操作,可是不同浏览器机制不一致,故而最好的办法还是利用修改className修改来避免多次回流重绘是最好的方案
- 把DOM离线后修改,比如:先把DOM给display:none (Reflow一次),然后你修改100次,再把他显示出来
- 不要把DOM结点的属性值放在一个循环里当成循环变量
5.1 例如:读取一个DOM的offsetHeight offsetWidth浏览器为了获取更加准确的DOM位置信息,会强制刷新回流的缓冲区域从而导致缓冲区域失效
var doms = [] //通过选择器选择出一个dom元素的数组 var domsTop = [] var clientHeight = document.body.clientHeight // 正确的做法 for(var i = 0;i< doms.lengt;i++){ // domsTop.push(document.body.clientHeight + i * 100 ) // 错误的写法,这样会导致浏览器的缓冲机制失效,代码会不够健壮 domsTop.push(clientHeight + i * 100 ) // 正确 }
- 不要使用table布局,可能很小的一个改动会造成整个table 的重新布局
30行table 修改最后一行 layout的时间将是1.15ms
30行div 修改最后一行 layout的时间将是0.1ms
- 动画实现的速度选择(合理选择适合的动画时间)
- 对于动画新建图层
使用translate:translateZ(0) + translate:translate3D(0,0, 0)
- 启用GPU硬件加速(使用translate3D)
9.1 GPU加速也不能过量使用,因为GPU和CPU之间的数据传输也会消耗性能
gpu加速其实在图层合成中也会消时间,所以在开启gpu加速这一点上,需要根据实际情况来使用
- 开启gpu加速方法,添加属性
translate:translateZ(0) translate:translate3D(0,0, 0)
浏览器存储
目的:
1. 理解localstorage cookie sessionstorage indexdb 的概念和使用
2. 学习理解pwa和service worker的应用
cookie
设计初衷:因为http请求是无状态的,所以需要cookie去维护客户端的状态(记录当前客户端在服务端的行为作记录)
- 用于浏览器端和服务器端的交互
- 客户端自身数据的存储
存储限制:
- 作为浏览器存储,大小4kb左右
- 需要设置过期时间 expire
httponly : true (不允许读写,用来防止黑客)
cookie中在相关域名下面 --- cdn的流量损耗
(有一些请求是不需要cookie的例如请求静态资源文件,但是http请求会携带cookie,如果域名是一致的话)
解决: cdn的域名和主站的域名要分开
LocalStorage
设计初衷就是为了用于浏览器的存储
特点
- 大小为5m左右
- 仅在客户端使用,不和服务端进行通信
- 接口封装较好
- 浏览器本地缓存方案(为了提升首屏渲染的效率)
SessionStorage
用于会话级别的浏览器存储(例如:一个填表的信息保存,一旦用户完成该会话就会清空,提高用户体验)
特点
- 大小为5m
- 仅在客户端使用,不和服务端进行通信
- 接口封装较好
- 对于表单信息的维护
IndexDb
IndexedDB 是一种低级API,用于客户端存储大量结构化数据(一般是存储在数据库中的),该api使用索引来实现对该数据的高性能搜索。虽然WebStorage 对于存储较少量的数据很有用,但对于存储更大量的结构化数据来说,这种方法不太有用。IndexDb提供了一个解决方案
- 为应用创建离线版本
学习连接
Service Workers
Service Workers 是一个脚本,浏览器独立于当前网页,将其在后台运行,为实现一些不依赖页面或者用户交互的特性打开了一扇大门,在未来这些特性包括推送信息,背景后台同步,geofencing(地理围栏定位),但它将推出的第一个首要特性,就是拦截和处理网络请求的能力,包括以编程方式来管理被缓存的响应(说白了就是独立于js主线程外的单独线程,减少js阻塞程度,和提高可拓展性)
使用拦截和处理网络请求的能力,去实现实现离线应用
使用 Service Worker 在后台运行同时能和页面通信的能力,去实现大规模后台数据处理
PWA
PWA是一种web app 新模型,并不是具体指某一种前沿的技术或者某一个单一的知识点,这是一个渐进式的Web App,是通过一系列新的Web特性,配合优秀的UI交互设计,逐步增强Web App 的用户体验(说白就是不同网络下产品的不同展示)
- 可靠: 在没有网络的环境中也能提供基本的页面访问,而不会出现"未连接到互联网"的页面
- 快速: 针对网页渲染及网络数据访问有较好的优化
- 融入(Engaging): 应用可以被增加到手机桌面,并且和普通应用一样有全屏和推送等特性
前端性能检测工具lighthouse
离线webApp案例-推特网