美文网首页
性能优化之从URL到页面展示

性能优化之从URL到页面展示

作者: Thehrdertheluck | 来源:发表于2019-12-22 10:42 被阅读0次

今天从性能优化的角度再来看看从URL到页面展示,前两篇是从URL到页面展示的流程说起,中间过程发生了什么,并没有突出性能优化点,当然若不知其中间发生了什么不知其原理又谈何性能优化,所以若是还没看过之前的两篇笔记,可以先结合之前两篇一起看。

《从输入URL到页面展示,这中间发生了什么?》
《浏览器页面的渲染流程》

为了整理这一篇文章,特意在掘金上购买了《前端性能优化原理与实践》小册(ps: 要有输入才有输出哈),里面主要是“从输入 URL 到页面加载完成,发生了什么?”作为引子开启话题,这个面试题从大处着手思考,就是两个重要知识维度,一是网络层面,二是渲染层面。往细处说前者牵涉到DNS(域名解析系统)、IP寻址、TCP连接、http请求与响应等;后者则是进程与线程概念、DOM树、层叠样式、重排与重绘及合成等。
小册里面有一张性能优化的思维导图贴出来分享给大家

网络层面性能优化

关于网络层面优化,映入眼帘应该是资源请求与加载,至于DNS域名解析、IP寻址、TCP连接这种网络基础设施我们前端领域也做不了任何优化,而关于资源请求与加载的优化,就有很多方面着手,比如源头上代码打包压缩、构建优化,就要连同webpack工程化去做相关方面优化工作了。在webpack 的优化方面主要体现两个方面:

  • webpack 的构建过程太花时间
  • webpack 打包的结果体积太大

webpack常见的优化方案

  • 利用DllPlugin构建常用的依赖库
  • 使用Happypack将 loader 由单进程转为多进程
  • Tree-Shaking摇树功能在打包时提前去除无用代码
  • 利用缓存加速二次构建速度
  • 按需加载
    核心在于require.ensure(dependencies, callback, chunkName)
  • 利用Gzip压缩
    在我们请求资源头里request headers加上一句:accept-encoding: gzip

相信还有其他的webpack优化方案,欢迎各位补充哦~

图片的优化

我们常说页面优化要从关键资源入手,其实忽略了页面优化关键在于图片的优化。不知道大家认不认同这一观点,可能对于做电商来说非常认同,因为做电商本质上就是做图片;但不管怎样,图片的优化肯定是我们前端领域性能优化重要一环。想想我们工作中的图片优化,是不是在图片大小和质量上做“权衡”,所谓的优化相当于在做“权衡”,牺牲图片质量追求体验和性能。

我们熟悉下图片的几种格式:

  • JPEG/JPG 特点:有损压缩、体积小、加载快、不支持透明
  • PNG-8 与 PNG-24 特点:无损压缩、质量高、体积大、支持透明
  • SVG 特点:文本文件、体积小、不失真、兼容性好
  • Base64 特点:文本文件、依赖编码、小图标解决方案
  • WebP 特点:支持有损压缩和无损压缩,全能型选手

在工作中以上几种格式相信都用过,其实图片的优化还有很多有待挖掘,若不结合工作深入,很难有自己深刻的见解。好比性能优化并不好学,根本原因在于前端技术复杂又日新月异,知识不成体系,难以切入。

缓存优化

接着来说页面的非图片资源加载优化,也就是资源缓存优化,一来减少网络 IO 消耗,二来提高访问速度。浏览器缓存是一种操作简单、效果显著的前端性能优化手段。

浏览器缓存机制

览器缓存机制有四个方面,它们按照获取资源时请求的优先级依次排列如下:

1、Memory Cache;2、Service Worker Cache ; 3、HTTP Cache ;4、Push Cache

  • MemoryCache
    指的是存在内存中的缓存。从优先级上来说,它是浏览器最先尝试去命中的一种缓存。从效率上来说,它是响应速度最快的一种缓存。

  • Service Worker
    是一种独立于主线程之外的 Javascript 线程。它脱离于浏览器窗体,因此无法直接访问 DOM。这样独立的个性使得 Service Worker 的“个人行为”无法干扰页面的性能,这个“幕后工作者”可以帮我们实现离线缓存、消息推送和网络代理等功能。我们借助 Service worker 实现的离线缓存就称为 Service Worker Cache。
    PS:大家注意 Server Worker 对协议是有要求的,必须以 https 协议为前提。

  • HTTP Cache
    是我们日常开发中最为熟悉的一种缓存机制。它又分为强缓存和协商缓存。优先级较高的是强缓存,在命中强缓存失败的情况下,才会走协商缓存。

强缓存的实现:从 (http1.0) expires 到 (http1.1) cache-control
协商缓存的实现:从 Last-Modified 到 Etag
协商缓存机制下,浏览器需要向服务器去询问缓存的相关信息,进而判断是重新发起请求、下载完整的响应,还是从本地获取缓存的资源。

关于http缓存下面贴一张权威的流程图:


  • Push Cache
    指的是 HTTP2 在 server push 阶段存在的缓存。因为知识比较新工作上没有用过,不做过多笔记整理。
浏览器本地存储Web Storage

最后到了浏览器的本地存储数据了,在HTML5之前一直是cookie,那是为了存储会话session状态,后面随着技术发展有了localStorage和sessionStorage,以满足丰富的页面数据缓存需要。关于web Storage他们之间的区别在于生命周期作用域

  • 生命周期:Local Storage 是持久化的本地存储,存储在其中的数据是永远不会过期的,使其消失的唯一办法是手动删除;而 Session Storage 是临时性的本地存储,它是会话级别的存储,当会话结束(页面被关闭)时,存储内容也随之被释放。

  • 作用域:Local Storage、Session Storage 和 Cookie 都遵循同源策略。但 Session Storage 特别的一点在于,即便是相同域名下的两个页面,只要它们不在同一个浏览器窗口中打开,那么它们的 Session Storage 内容便无法共享。

除了耳熟能详的cookie、localStorage、sessionStorage,还有稀疏平常的浏览器数据库IndexDB,因为工作中没有用过,没有太多感触。不过感兴趣的还是可以了解一下,当然也需要有一个感性认识,万一后面数据复杂需要本地存储要用到浏览器数据库呢。
参考阮一峰的网络日志《浏览器数据库 IndexedDB 入门教程》

渲染层面的优化

梳理完了网络层面的优化,紧接着来看看渲染层面的优化部分。先回顾下浏览器渲染进程各个阶段的工作流程图


其中我们重点关注被HTML解释器解析的DOM、被CSS解释器解析的计算属性style、图层布局模块、图层绘制模块、视图合成模块。因为这些地方我们是可以做相关优化的,下面就从这5个方面一一梳理优化的点。


DOM的优化

对于DOM的优化并不陌生,主要在于减少DOM节点的嵌套深度、以及DOM节点的操作,以此避免渲染树的重排与重绘。

  • 重排:当我们对 DOM 的修改引发了 DOM 几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性(其他元素的几何属性和位置也会因此受到影响),然后再将计算的结果绘制出来。这个过程也叫回流。
  • 重绘:当我们对 DOM 的修改导致了样式的变化、却并未影响其几何属性(比如修改了颜色或背景色)时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式,会跳过重排环节。

CSS的优化

关于CSS的优化,主要体现在书写规范上面、CSS资源加载顺序上面、以及CSS动画上面。

CSS的样式规则

首先要知道CSS引擎查找样式表,对每条规则都按从右到左的顺序去匹配。知道这一知识点很重要,当我们在写样式的时候,就要避免使用通配符*,或者是元素标签,尽量使用选择器;另外要合理使用嵌套,不要多层深度嵌套(最高三层嵌套),尽可能使用类来关联每一个标签元素。

CSS的阻塞

根据上面的页面渲染流程图,我们知道CSS的加载阻塞是会影响到页面渲染的,也就是说:

CSS 是阻塞渲染的资源。需要将它尽早、尽快地下载到客户端,以便缩短首次渲染的时间。

对于CSS的阻塞优化,小册里面总结得很好,做到两点即可:一是尽早,将 CSS 放在 head 标签里;二是尽快,启用 CDN 实现静态资源加载速度的优化。

CSS的动画

CSS的动画方面优化主要是在合成环节上优化,例如使用transform动画会跳过渲染流程中的重排与重绘环节,直接进入合成阶段,因为transform属性是元素的既不布局也不绘制的属性。另外,合成相对于重排重绘来说,会大大提升绘制效率。


JS的优化

关于JS对于页面渲染的优化,主要也是阻塞和对DOM的操作,优化的点也在于减少重排和重绘。

JS的加载方式
  • 正常模式
<script src="index.js"></script>

一般我们会把js文件置于body结束标签的位置,是根据浏览器渲染原理来的,避免阻塞。

  • async 模式
<script async src="index.js"></script>

async 模式下,JS 不会阻塞浏览器做任何其它的事情。它的加载是异步的,当它加载结束,JS 脚本会立即执行。

  • defer 模式
<script defer src="index.js"></script>

defer 模式下,JS 的加载是异步的,执行是被推迟的。等整个文档解析完成、DOMContentLoaded 事件即将被触发时,被标记了 defer 的 JS 文件才会开始依次执行。

JS操作DOM优化

在JS中尽量减少 DOM 操作,避免过度渲染。比如:

let container = document.getElementById('container')
let content = ''
for(let count=0;count<10000;count++){ 
  // 先对内容进行操作
  content += '<span>我是一个小测试</span>'
} 
// 内容处理好了,最后再触发DOM的更改
container.innerHTML = content

另外可以用DocumentFragment给DOM分压,减少DOM的操作。

let container = document.getElementById('container')
// 创建一个DOM Fragment对象作为容器
let content = document.createDocumentFragment()
for(let count=0;count<10000;count++){
  // span此时可以通过DOM API去创建
  let oSpan = document.createElement("span")
  oSpan.innerHTML = '我是一个小测试'
  // 像操作真实DOM一样操作DOM Fragment对象
  content.appendChild(oSpan)
}
// 内容处理好了,最后再触发真实DOM的更改
container.appendChild(content)
JS中规避重排与重绘
  • 利用变量缓存起来,避免频繁改动
    有时我们想要通过多次计算得到一个元素的布局位置,我们这样做:
// 缓存offsetLeft与offsetTop的值
const el = document.getElementById('el') 
let offLeft = el.offsetLeft, offTop = el.offsetTop
// 在JS层面进行计算
for(let i=0;i<10;i++) {
  offLeft += 10
  offTop  += 10
}
// 一次性将计算结果应用到DOM上
el.style.left = offLeft + "px"
el.style.top = offTop  + "px"
  • 避免逐条改变样式,使用类名去合并样式
const container = document.getElementById('container')
container.style.width = '100px'
container.style.height = '200px'
container.style.border = '10px solid red'
container.style.color = 'red'
// 使用类名
const container = document.getElementById('container')
container.classList.add('basic_style')
  • 将 DOM “离线”
    操作DOM可以先display:none,在操作它的属性,完成后再display:block显示出来。对于频繁操作改变它的属性来说也是不错的优化方法。

  • Flush 队列:浏览器并没有那么简单

// 这段代码里,浏览器进行了多少次的回流或重绘呢?
let container = document.getElementById('container')
container.style.width = '100px'
container.style.height = '200px'
container.style.border = '10px solid red'
container.style.color = 'red'

上面代码片段浏览器会进行4次重排或重绘操作吗?我们自己可以动手试一试,其实并不然哦,因为现代浏览器是很聪明的。浏览器自己也清楚,如果每次 DOM 操作都即时地反馈一次回流或重绘,那么性能上来说是扛不住的。于是它自己缓存了一个 flush 队列,把我们触发的回流与重绘任务都塞进去,待到队列里的任务多起来、或者达到了一定的时间间隔,或者“不得已”的时候,再将这些任务一口气出队。所以上面就算我们进行了 4 次 DOM 更改,也只触发了一次 Layout 和一次 Paint。

小结

关于性能优化,真有一种说不清道不明的感觉。这里只是“从URL到页面展示”的角度来学习性能优化的知识,相信内容有很多是浮于表面,具体业务场景肯定是更加复杂多变,所以说前端的性能优化点是错综复杂,比较综合考验个人的工作能力。Anyway,梳理就到此为止,总之,性能优化是一个路漫漫其修远兮的过程,痛并快乐着做吧~

相关文章

  • 性能优化之从URL到页面展示

    今天从性能优化的角度再来看看从URL到页面展示,前两篇是从URL到页面展示的流程说起,中间过程发生了什么,并没有突...

  • 一文串联 HTTP、TCP、IP、以太网

    最近部门组织了一次前端性能优化交流会,大家从输入页面 URL 到最终页面展示内容这个过程提出了许多优化点。但同时发...

  • 前端性能优化原理与实践(一)

    摘自前端性能优化原理与实践 网络层面的性能优化 我们从输入 URL到显示页面这个过程中,涉及到网络层面的,有三个主...

  • iOS性能优化之页面加载速率

    iOS性能优化之页面加载速率 iOS性能优化之页面加载速率

  • 从URL到页面展示

    浏览器与服务器的交互 在浏览器输入URL 浏览器根据URL查找对应的域名的IP地址,查找顺序浏览器缓存-本机缓存-...

  • 从URL到页面展示

    从我们输入一行网址到浏览器加载出一个漂亮的界面这个过程中发生了什么呢? 1.一些名词解释 URL(Uniform ...

  • 从URL到展示页面

    (本文内容为作者个人理解,有出现错误的地方欢迎指出) 第一步:在浏览器输入URL URL:统一资源定位符,定位互联...

  • 从url到页面展示

    从url到页面展示总体来说分为以下几个过程: DNS解析 TCP连接 服务器处理响应请求 浏览器解析渲染页面 DN...

  • 前端性能优化

    前端性能优化 前端性能优化是指用户从开始访问网站开始到整个页面完整的展示出来的过程中,我们可以通过各种的优化策略和...

  • 从输入url到页面展示到底发生了什么

    从输入url到页面展示到底发生了什么

网友评论

      本文标题:性能优化之从URL到页面展示

      本文链接:https://www.haomeiwen.com/subject/cdrvnctx.html