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

2019-12-22  本文已影响0人  Thehrdertheluck

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

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

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

网络层面性能优化

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

webpack常见的优化方案

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

图片的优化

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

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

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

缓存优化

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

浏览器缓存机制

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

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

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

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


浏览器本地存储Web Storage

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

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

渲染层面的优化

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


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


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结束标签的位置,是根据浏览器渲染原理来的,避免阻塞。

<script async src="index.js"></script>

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

<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')
// 这段代码里,浏览器进行了多少次的回流或重绘呢?
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,梳理就到此为止,总之,性能优化是一个路漫漫其修远兮的过程,痛并快乐着做吧~

上一篇 下一篇

猜你喜欢

热点阅读