前端性能优化

前端性能优化点


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

资源的合并与压缩

  • 实际是就是 减少http请求数量(合并)
  • 减少请求资源的大小(压缩)

html压缩

  1. 使用再线网站进行压缩
  2. nodejs提供htmn-minifier工具
  3. 后端模板引擎渲染压缩

css压缩

  1. 无效代码删除
  2. css语义合并

js压缩与混乱

  1. 无效字符的删除
  2. 剔除注释
  3. 代码语义的压缩和优化
  4. 代码保护

文件合并

  1. 使用工具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的异步加载能力

理解懒加载和预加载的原理

本质:权衡浏览器本身具有的加载资源的能力。(个人觉得其实就是为了提升用户体验,和项目本身的性能)


懒加载
  1. 图片进入可视区域之后请求图片资源
  2. 减少无效资源的加载
  3. 并发加载的资源过多会阻塞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()

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

可以设置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变慢

解决办法

  1. 避免使用触发重绘,回流的css属性
  2. 将频繁重绘回流的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的过程
  1. 获取DOM后分割为多个图层
  2. 对每个图层的节点计算样式结果(Recalculate style --- 样式重计算)
  3. 为每个节点生成图形和位置(Layout -- 回流和重布局)
  4. 将每个节点绘制填充到图层位图中(Paint Setup和Paint -- 重绘 )
  5. 图层作为纹理上传至GPU
  6. 符合多个图层到页面上生成最终屏幕图像(Composite Layers图层重组)
Chrome 创建图层的条件

不过需要注意的是图层过多也会影响性能,因为图层合成也会消耗比较多的性能

  • 3d或透视变换css属性(perspective transform)
  • 使用加速视频解码的<video>节点
  • 拥有3d(WebGL)上下文或加速的2D上下文的<canvas>节点
  • 混合插件(如Flash)
  • 对自己的opacity做css动画或使用一个动画webkit变换的元素
  • 拥有加速css过滤器的元素(例如:translate3D)
  • 元素有一个包含复合层的后代节点 (一个元素拥有一个子元素,该子元素在自己层里)
  • 元素有一个z-index 较低且包含一个复合层的兄弟元素(即该元素在复合层上面渲染 比如:gif)
总结(重绘与回流)的优化点
  1. 用translate 替代 top 改变

使用top会触发回流,而使用translate则不会触发回流

  1. 用opacity 替代 visibility

使用opacity替代visibility的时候需要注意的是,必须要让该dom独立称为一个单独的图层(即:添加transfrom:transfromz(0) ),否则浏览器仍然会有回流和重绘

  1. 不要一条一条的修改 DOM 的样式,预先定义好 class,然后修改DOM的className

虽然直接一条条修改也有可能只执行一次回流,但是这其实利用的是浏览器的缓冲机制完成的操作,可是不同浏览器机制不一致,故而最好的办法还是利用修改className修改来避免多次回流重绘是最好的方案

  1. 把DOM离线后修改,比如:先把DOM给display:none (Reflow一次),然后你修改100次,再把他显示出来
  2. 不要把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 ) // 正确
       }
  1. 不要使用table布局,可能很小的一个改动会造成整个table 的重新布局
    30行table 修改最后一行 layout的时间将是1.15ms
    30行div 修改最后一行 layout的时间将是0.1ms
  2. 动画实现的速度选择(合理选择适合的动画时间)
  3. 对于动画新建图层
    使用translate:translateZ(0) + translate:translate3D(0,0, 0)
  4. 启用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
cookie

设计初衷:因为http请求是无状态的,所以需要cookie去维护客户端的状态(记录当前客户端在服务端的行为作记录)

  1. 用于浏览器端和服务器端的交互
  2. 客户端自身数据的存储

存储限制:

  1. 作为浏览器存储,大小4kb左右
  2. 需要设置过期时间 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 在后台运行同时能和页面通信的能力,去实现大规模后台数据处理

ServiceWorker
PWA

PWA是一种web app 新模型,并不是具体指某一种前沿的技术或者某一个单一的知识点,这是一个渐进式的Web App,是通过一系列新的Web特性,配合优秀的UI交互设计,逐步增强Web App 的用户体验(说白就是不同网络下产品的不同展示)

  • 可靠: 在没有网络的环境中也能提供基本的页面访问,而不会出现"未连接到互联网"的页面
  • 快速: 针对网页渲染及网络数据访问有较好的优化
  • 融入(Engaging): 应用可以被增加到手机桌面,并且和普通应用一样有全屏和推送等特性
    前端性能检测工具 lighthouse
    离线webApp案例-推特网
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343