掘金小册《前端性能优化原理与实践》读书笔记

前端性能优化:

以下文章内容是根据掘金小册《前端性能优化原理与实践》整理的

网络层面的优化

  • 1、减少请求次数;
  • 2、减少单次请求花费的时间;

webpack相关优化;

  • 1、按需加载关键:require.ensure(dependencies, callback, chunkName)
  • 2、Gzip压缩原理:在一个文本文件中找出一些重复出现的字符串、临时替换它们,从而使整个文件变小。根据这个原理,文件中代码的重复率越高,那么压缩的效率就越高,使用 Gzip 的收益也就越大。反之亦然。

图片优化

1、常用的图片格式介绍:
  • 1)jpg图片:有损压缩、体积小、加载快、不支持透明
    • 常用于色彩丰富的图片;经常作为大的banner图,背景图,轮播图;既可以保住图片质量,又降低了图片体积;
      (把图片体积压缩至原有体积的 50% 以下时,JPG 仍然可以保持住 60% 的品质)
    • 缺点:不支持透明度处理;
  • 2)PNG-8,PNG-24:无损压缩,质量高,体积大,支持透明;
    • 常用于呈现小的logo,颜色简单且对比强烈的图片或背景等;
  • 3) SVG:体积小,不失真,兼容性好,文本文件;
  • 4)base64:文本文件,依赖编码,小图标解决方案;
    • base64并非是一种图片格式,而是一种编码方式;和雪碧图一样,作为小图标的解决方案而存在;

    • 减少加载网页图片时对服务器的请求次数;作为雪碧图的补充方式而存在;

    • Base64 是一种用于传输 8Bit 字节码的编码方式,通过对图片进行 Base64 编码,可以直接将编码结果写入 HTML 或者写入 CSS,从而减少HTTP 请求的次数

    • 雪碧图(CSS Sprites)介绍:

      • 一种将小图标和背景图像合并到一张图片上,然后利用 CSS 的背景定位来显示其中的每一部分的技术
    • 为什么大图片不使用base64?

      • 因为经过base64编码后,图片大小会膨胀为原文件的4/3;(由base64编码原理决定);所以大文件编码后直接放在html/css文件中,体积明显增大,与节省的http请求开销相比不划算,所以不这么干;
    • base64使用场景?

      • 图片尺寸很小,(如<2kb);
      • 图片无法以雪碧图的形式与其它小图结合(优先使用雪碧图减少http请求,base64作为补充方案;)
      • 图片的更新频率较低;(不需要我们重复编码或修改文件内容,维护成本低)
  • 5)webp:2010年被提出,集多种图片格式的优点于一身;
    • 缺点:新生事物,兼容性一般;增加服务器负担,占用更多的计算资源;
    • 使用场景:由于兼容性差,所以使用时需考虑不支持webp格式浏览器(如safari)中的显示方案;

浏览器缓存机制介绍和缓存策略剖析

  • 1)http缓存;分为强缓存,协商缓存;
  • 2)memory cache:内存中的缓存;是浏览器最先尝试的一种缓存,响应速度最快;和渲染进程生死相依,tab关闭,缓存就不在了;
  • 3)service wroker cache(离线缓存):
    • service worker:独立于主线程之外的JS线程,脱离浏览器窗口,因此无法直接访问DOM; 可实现离线缓存,消息推送,网络代理等功能;
    • service wroker 必须以https协议为前提
  • 4) Push Cache:http2在server push阶段存在的缓存;
    • 更多内容查看博客:https://jakearchibald.com/2017/h2-push-tougher-than-i-thought/

本地存储;Cookie,Web Storage,indexDB

  • 1)Cookie: 本职工作并非本地存储,而是维持状态;(因为http是无状态协议,服务端在接收到客户端的请求返回响应后就结束了,不会记录客户端的相关信息;cookie可携带用户信息在客户端和服务端之间飞来飞去)
    • 只能存储少量信息,上限为 4kb;
    • Cookie紧跟域名,同一个域名下的所有请求,都会携带Cookie值;
  • 2)Web Storage,分为LocalStorage,SessionStorage,
    • localStorage和sessionStorage的区别??
      • 生命周期
        • localStorage:持久化的本地存储,存储在其中的数据永不会过期,除非手动删除;
        • sessionStorage:临时性的本地存储,会话级,会话结束(即浏览器窗口关闭)存储的内容也随之清空;
      • 作用域
        • 都遵守同源策略,但sessionStorage即便是在同一个域名下的两个页面,只要是在不同的浏览器窗口打开,那么它们存储的内容就不共享;
    • 存储上限根据浏览器的不同,容量可达5M-10M,通常不超过5M;
    • 只存在于浏览器端,不与服务端进行通信;
    • 只能存储字符串;
      3)indexDB:运行在浏览器上的非关系型数据库,理论上说,没有存储上限(一般不小于250M),不仅可存储字符串,还可存储二进制数据;

CDN的缓存与回源机制解析

  • CDN:(内容分发网络)指的是一组分布在各个地区的服务器;这些服务器存储着数据的副本,因此服务器可以根据哪些服务器与用户的距离最近,来满足数据的请求;(提供高速服务,较少受到高流量影响)

    • 提升首次请求的响应力,
    • 核心功能点:缓存,回源
      • 缓存:把资源copy一份到CDN服务器的过程;
      • 回源:CDN服务器上没有这个资源(一般是缓存的资源过期了),转头向根服务器(或它上一级服务器)去要这个资源的过程
    • 静态资源走CDN,可提高加载速度;(在大公司中是默认规定)
    • 以淘宝网首页为例,业务服务器和CDN服务器的域名是不同的,这样就可以避免加载CDN服务器上的静态资源时也需要将Cookie传来传去,节省开销;(因为同一个域名下所有的请求都会携带该域名下的Cookie值;)
    • 怎样在浏览器中查看该请求是否是从CDN服务器上获取的资源??
      • 在响应头中查看是否有以下字段:
        • X-Cache:CDN是否支持缓存;
        • X-Swift-CacheTime:资源在cdn上的可以缓存多久,即资源在CDN上缓存的总时间;
        • X-Swift-SaveTime:资源在是什么时间缓存到CDN节点的;(时间点)
        • Age: 文件资源在CDN上缓存的时间,(单位是秒),如果值为0说明缓存的资源过期了,需要回源;
        • MISS:CDN节点上无该文件的缓存,回源请求。
        • HIT:表示已缓存。
  • 浏览器缓存,本地缓存解决的都是第二次请求时响应慢的问题,对于首次请求这些招式是无效的;

服务端渲染的探索与实践

  • 1)运行机制:当用户第一次请求页面时,服务器把需要的页面或组件渲染成html字符串,然后把它返回给客户端;客户端接收到响应内容后可以直接渲染然后呈现给用户的html内容,不需要为了生成dom内容再去跑一遍JS代码;
  • 2)解决了什么问题:SEO搜索不到;首拼加载速度过慢;
  • 3)应用场景:
    • 因为用户客户端数量可以有很多,但是服务器资源是稀少宝贵的,所以把很多台浏览器的渲染压力集中起来,分散给相比之下数量并不多的服务器,服务器肯定是承受不住的;
    • 先用低成本的首屏渲染和SEO的优化方案,除非网页对性能要求太高,以至于所有招式都用完了,性能还是不满意,这时可以考虑申请多台服务器,使用服务端渲染;

浏览器背后的运行机制

1、浏览器内核
  • 1)浏览器的心即浏览器的内核;不同浏览器的差异性正是因为内核不同而导致的,内核决定了浏览器解析网页语法的方式;
  • 2)内核可分为两部分:渲染引擎(HTML解析器,CSS解析器,布局,网络,存储,图形,音视频,图片解码器等零部件) + JS引擎;
    • a、HTML解析器:将html文档经过词法分析输出浏览器可以理解的DOM树;
    • b、CSS解析器:解析CSS文档,生成规则样式,输出CSSOM树;(计算dom数中每个节点的具体样式)
      • 开始解析html后,解析到link标签或者style标签时,CSSOM的构建才开始,(CSS解析和DOM的解析过程是并行的;)
    • c、图层布局计算模块:布局计算每个对象的精确位置和大小;(计算dom树中可见节点的几何位置信息,生成可见元素的布局树)
    • d、视图绘制模块:进行具体节点的图像绘制,将像素渲染到屏幕上;
    • e、JS引擎:编译执行JS代码;
  • 3)常见的浏览器内核:Trident(IE),Gecko(火狐),Blink(Chrome,Opera),Webkit(Safari)
    • Chrome2013年以前使用的是Webkit内核;13年以后使用的是Blink内核;(但Blink其实也是基于Webkit衍生来的一个分支)
2、浏览器的渲染过程
  • 1)渲染过程:渲染引擎根据html的文件描述构建相应的数学模型,调用浏览器的各个零部件,将网页资源代码转为图像结构;
  • 2)渲染过程中性能提升的方案:
    • CSS选择器书写习惯;
      • a、避免使用通配符*,只对用到的元素进行选择;
      • b、少用标签选择器,如果可以使用class选择器代替;
      • c、id和class选择器不应该被多余的标签选择器拖后腿;
      • d、减少嵌套;
    • 尽早(将CSS放在head标签中)尽快(使用CDN实现静态资源加载)的加载CSS资源,因为CSS是阻塞渲染的资源;
    • JS的执行会阻塞CSS和html的解析(因为JS引擎是独立于渲染引擎存在的,在解析html的过程中,当遇到了script标签就会暂停渲染过程,将控制权交给JS引擎,JS引擎对外部的JS需要先下载再执行对内联的JS直接执行,等JS引擎执行完毕会将控制权交给渲染引擎,继续CSSOM和DOM的构建;)
      • JS的加载方式:
        • 正常模式(会阻塞浏览器)
        • 异步加载模式(async,defer):不会阻塞浏览器做其他的事情,
      • 当脚本文件与DOM元素和其他脚本之间依赖关系不强时可选用async;当脚本文件依赖DOM元素和其他脚本的执行结果时选用defer;

DOM优化原理与基本实践;

  • 为什么DOM慢?
    • 1)在JS世界里一切都是简单快速的,但是DOM操作是两个模块间的协作,JS引擎和渲染引擎"跨界交流"时,会依赖桥接接口作为桥梁,产生开销;(“收过桥费”)
    • 2)JS对DOM的修改会引发样式更迭(重排即回流,重绘)
  • 如何使DOM变快?
    • 1)使用缓存变量对访问的dom节点进行缓存(少交过桥费)
    • 2)减少不必要的DOM更改,可将插入节点的dom内容先准备好,然后一次性插入父节点中(减少回流/重绘)
    • 3)使用Dom Fragment缓存批量化的DOM操作;(Dom Fragment本质是脱离真实DOM树的容器,它的变化不会触发dom树的重新渲染,且不会对性能产生影响)

事件循环与异步更新策略

  • 事件循环的执行顺序:宏任务-微任务-UI渲染;(当我们需要在异步任务中实现DOM修改时,把它包装成微任务是较好的;)
    • 常见的宏任务:setImmediate,setInterval,setTimeout,script(整体代码)
    • 常见的微任务:process.nextTick,Promise,MutationObserver
  • 异步更新可以避免过度渲染;

回流与重绘

  • 1)基本概念:
    • 回流:对dom的修改引起元素几何尺寸的变化(比如修改元素的宽,高或隐藏元素等)
    • 重绘:对dom的修改导致了样式的变化,但是并不影响其几何属性;(比如修改颜色,背景色,可见性(visibility: hidden) 等)
    • (重绘不一定导致回流,但是回流一定会导致重绘)
  • 2)什么情况下会触发回流?
    • a、改变DOM的几何树形;
    • b、改变dom树的结构,(即对节点的增减,移动等操作)
    • c、获取一些特定的属性值,因为这些值需要通过即时计算得到,所以浏览器为了准备,即时的拿到这些值,也会触发回流操作;
      • 如:offsetTop,offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight
  • 3)如何避免回流?
    • a、使用JS变量将节点缓存起来,避免频繁改动;
    • b、避免逐条修改样式,使用类名合并样式;
    • c、将DOM离线;(即对dom进行操作前,先将dom节点display: none掉,然后再将其display: block出来)
  // 不推荐
  const container = document.getElementById('container')
  container.style.width = '100px'  // 回流
  container.style.height = '200px'  // 回流
  container.style.border = '10px solid red' // 回流
  container.style.color = 'red' // 重绘
  
  // 推荐
  .container_box {
    width: 100px;
    height: 200px;
    border: 10px solid red;
    color: red
  }
  <script>
    const container = document.getElementById('container')
    container.classList.add('container_box')
  </script>

优化首屏体验——Lazy-Load初探

  • 懒加载(延迟加载):针对图片加载时机的优化,在页面打开时只把首屏的图片资源加载出来,等用户下拉的瞬间再即时去请求下面的图片;
    // 图片懒加载,即只显示可视屏幕的图片
    let imgs = document.getElementsByTagName('img')
    let count = 0
    // 获取可视屏幕的高度
    const clientHg = window.innerHeight || document.documentElement.clientHeight
    // 定义懒加载函数
    function lazyFn() {
      for(let i=count; i<imgs.length;i++) {
        // i从count开始,可以避免查找已经渲染了真实图片的img节点
        let distance = clientHg - imgs[i].getBoundingClientRect().top
        if (distance >= 0) {
          // 如果可视区域的高度 >= 元素顶部距离可视区域顶部的高度时,说明元素露出
          imgs[i].src = imgs[i].getAttribute('data-src')
          count++
        }
      }
    }
    window.onload = () => {
      // 页面加载完成先执行一次
      lazyFn()
    }
    window.addEventListener('scroll', lazyFn, false)

事件节流(throttle)与事件防抖(debounce)

  • throttle: “第一个人说了算”;在一定的时间范围内,只执行一次事件回调,如果在这段时间内,事件被触发了则直接忽略;
  • debounce: “最后一个人说了算”,在某段时间内,不管触发了多少次回调,都只认最后一次;如果在这段时间内,时间被再次触发了,则重新开始计时;
// 使用throttle优化debounce
// fn是我们需要包装的事件回调, delay是时间间隔的阈值
function throttle(fn, delay) {
  // last为上一次触发回调的时间, timer是定时器
  let last = 0, timer = null
  // 将throttle处理结果当作函数返回
  
  return function () { 
    // 保留调用时的this上下文
    let context = this
    // 保留调用时传入的参数
    let args = arguments
    // 记录本次触发回调的时间
    let now = +new Date()
    
    // 判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值
    if (now - last < delay) {
    // 如果时间间隔小于我们设定的时间间隔阈值,则为本次触发操作设立一个新的定时器
       clearTimeout(timer)
       timer = setTimeout(function () {
          last = now
          fn.apply(context, args)
        }, delay)
    } else {
        // 如果时间间隔超出了我们设定的时间间隔阈值,那就不等了,无论如何要反馈给用户一次响应
        last = now
        fn.apply(context, args)
    }
  }
}

性能检测: Performance 和 LightHouse

前端内存泄漏排查——Chrome浏览器的Performance面板使用
  • 17
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wen_文文

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值