从url到页面渲染:我们能做哪些优化 – Python量化投资

从url到页面渲染:我们能做哪些优化

在前端面试中,有一道经典的面试题:请描述一下从url输入到页面渲染的过程。这道题很考察候选人的能力和知识深度,
今天我们以这个问题为基础,讲一下在此过程中能做哪些优化。

从输入 URL 到页面加载完成的过程

  • 首先做 DNS 查询,如果这一步做了智能 DNS 解析的话,会提供访问速度最快的 IP 地址回来

  • 接下来是 TCP 握手,应用层会下发数据给传输层,这里 TCP 协议会指明两端的端口号,然后下发给网络层。网络层中的 IP 协议会确定 IP 地址,并且指示了数据传输中如何跳转路由器。然后包会再被封装到数据链路层的数据帧结构中,最后就是物理层面的传输了

  • TCP 握手结束后会进行 TLS 握手,然后就开始正式的传输数据

  • 数据在进入服务端之前,可能还会先经过负责负载均衡的服务器,它的作用就是将请求合理的分发到多台服务器上,这时假设服务端会响应一个 HTML 文件

  • 首先浏览器会判断状态码是什么,如果是 200 那就继续解析,如果 400 或 500 的话就会报错,如果 300 的话会进行重定向,这里会有个重定向计数器,避免过多次的重定向,超过次数也会报错

  • 浏览器开始解析文件,如果是 gzip 格式的话会先解压一下,然后通过文件的编码格式知道该如何去解码文件

  • 文件解码成功后会正式开始渲染流程,先会根据 HTML 构建 DOM 树,有 CSS 的话会去构建 CSSOM 树。如果遇到 script 标签的话,会判断是否存在 async 或者 defer ,前者会并行进行下载并执行 JS,后者会先下载文件,然后等待 HTML 解析完成后顺序执行,如果以上都没有,就会阻塞住渲染流程直到 JS 执行完毕。遇到文件下载的会去下载文件,这里如果使用 HTTP 2.0 协议的话会极大的提高多图的下载效率。

  • 初始的 HTML 被完全加载和解析后会触发 DOMContentLoaded 事件

  • CSSOM 树和 DOM 树构建完成后会开始生成 Render 树,这一步就是确定页面元素的布局、样式等等诸多方面的东西

  • 在生成 Render 树的过程中,浏览器就开始调用 GPU 绘制,合成图层,将内容显示在屏幕上了

http优化

增加安全性:用https代替http

http详解

http是超文本传输协议,但是在信息传输过程中,由于通信使用的是明文,可能存在信息被窃取和篡改的问题。

可以使用安全的https替代http

详见Carlo大佬的博文 细说https

减少请求次数:浏览器缓存策略

当发送http请求时,浏览器首先会按顺序检查以下四项是否存在缓存

  • Memory Cache
  • Service Worker Cache
  • HTTP Cache
  • Push Cache

MemoryCache

MemoryCache,是指存在内存中的缓存。从优先级上来说,它是浏览器最先尝试去命中的一种缓存。从效率上来说,它是响应速度最快的一种缓存。浏览器秉承的是“节约原则”,我们发现,Base64 格式的图片,几乎永远可以被塞进 memory cache,这可以视作浏览器为节省渲染开销的“自保行为”;此外,体积不大的 JS、CSS 文件,也有较大地被写入内存的几率——相比之下,较大的 JS、CSS 文件就没有这个待遇了,内存资源是有限的,它们往往被直接甩进磁盘。

Service Worker Cache

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

HTTP Cache

它又分为强缓存和协商缓存。优先级较高的是强缓存,在命中强缓存失败的情况下,才会走协商缓存。

Push Cachae

Push Cache 是指 HTTP2 在 server push 阶段存在的缓存。

  • Push Cache 是缓存的最后一道防线。浏览器只有在 Memory Cache、HTTP Cache 和 Service Worker Cache 均未命中的情况下才会去询问 Push Cache。
  • Push Cache 是一种存在于会话阶段的缓存,当 session 终止时,缓存也随之释放。
  • 不同的页面只要共享了同一个 HTTP2 连接,那么它们就可以共享同一个 Push Cache。

关于浏览器的强缓存和协商缓存,详见Carry的博文 前端静态资源缓存

关于sevice Worker缓存, 详见Carry的博文 前端静态资源缓存之sevice worker

减少单次请求所花费的时间: CDN

CDN 的核心点有两个,一个是缓存,一个是回源。

“缓存”就是说我们把资源 copy 一份到 CDN 服务器上这个过程,“回源”就是说 CDN 发现自己没有这个资源(一般是缓存的数据过期了),转头向根服务器(或者它的上层服务器)去要这个资源的过程。

CDN 往往被用来存放静态资源。所谓“静态资源”,就是像 JS、CSS、图片等不需要业务服务器进行计算即得的资源。而“动态资源”,顾名思义是需要后端实时动态生成的资源,较为常见的就是 JSP、ASP 或者依赖服务端渲染得到的 HTML 页面。

那“非纯静态资源”呢?它是指需要服务器在页面之外作额外计算的 HTML 页面。具体来说,当我打开某一网站之前,该网站需要通过权限认证等一系列手段确认我的身份、进而决定是否要把 HTML 页面呈现给我。这种情况下 HTML 确实是静态的,但它和业务服务器的操作耦合,我们把它丢到CDN 上显然是不合适的。

另外,CDN的域名必须和主业务服务器的域名不一样,要不,同一个域名下面的Cookie各处跑,浪费了性能流量的开销,CDN域名放在不同的域名下,可以完美地避免了不必要的 Cookie 的出现!

渲染优化

浏览器的渲染机制一般分为以下几个步骤:

  • 处理 HTML 并构建 DOM 树。
  • 处理 CSS 构建 CSSOM 树
  • 将 DOM 与 CSSOM 合并成一个渲染树。
  • 根据渲染树来布局,计算每个节点的位置。
  • 调用 GPU 绘制,合成图层,显示在屏幕上

在渲染dom的时候,浏览器实际上所做的工作是

  • 获取DOM后分割为多个图层
  • 对每个图层的节点计算样式结果(Recalculate style–样式重计算)
  • 为每个节点生成图形和位置(Layout–回流和重布局)
  • 将每个节点绘制填充到图层位图中(Paint Setup和Paint–重绘)
  • 图层作为纹理上传至GPU
  • 复合多个图层到页面上生成最终屏幕图像(Composite Layers–图层重组)

服务端渲染

css优化建议

CSS 是阻塞的资源。浏览器在构建 CSSOM 的过程中,不会渲染任何已处理的内容。即便 DOM 已经解析完毕了,只要 CSSOM 不 OK,那么渲染这个事情就不 OK。我们将 CSS 放在 head 标签里 和尽快 启用 CDN 实现静态资源加载速度的优化。
CSS 选择符是从右到左进行匹配的,比如 #myList li {}实际开销相当高。

  • 避免使用通配符,只对需要用到的元素进行选择。
  • 关注可以通过继承实现的属性,避免重复匹配重复定义。
  • 少用标签选择器。如果可以,用类选择器替代。错误:#dataList li{} 正确:.dataList{}
  • 不要画蛇添足,id 和 class 选择器不应该被多余的标签选择器拖后腿。错误:.dataList#title 正确:#title
  • 减少嵌套。后代选择器的开销是最高的,因此我们应该尽量将选择器的深度降到最低(最高不要超过三层),尽可能使用类来关联每一个标签元素。

js阻塞和Event Loop

JS 引擎是独立于渲染引擎存在的。我们的 JS 代码在文档的何处插入,就在何处执行。当 HTML 解析器遇到一个 script 标签时,它会暂停渲染过程,将控制权交给 JS 引擎。

JS 引擎对内联的 JS 代码会直接执行,对外部 JS 文件还要先获取到脚本、再进行执行。等 JS 引擎运行完毕,浏览器又会把控制权还给渲染引擎,继续 CSSOM 和 DOM 的构建。

我们将耗时的js操作放在页面的尾部,等待html和css渲染完毕再执行js防止js阻塞页面渲染。

js 宏任务和微任务

  • JS中分为同步任务和异步任务,同步任务在执行栈中执行,异步任务的回调被放在任务队列中,等待执行栈空闲时执行。
    setTimeout/setInterval,XHR/fetch的回调函数都属于异步任务。

  • 任务栈执行一次的过程属于一次宏任务,在一个宏任务执行结果后,在下一个宏任务执行前,GUI渲染线程开始工作,对页面进行渲染。

  • 微任务是在一次宏任务执行完毕之后,GUI线程执行之前执行的任务。当宏任务执行完,会在渲染前,将执行期间所产生的所有微任务都执行完.
    Promise,process.nextTick等,属于微任务

合理的安排宏任务微任务以及dom操作的顺序,可以尽可能的减少无效的dom操作。

更多关于js和Event Loop的知识,请参考云中桥大佬的博文 从多线程到Event Loop全面梳理

回流和重绘

  • 回流:当我们对 DOM 的修改引发了 DOM 几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性(其他元素的几何属性和位置也会因此受到影响),然后再将计算的结果绘制出来。这个过程就是回流(也叫重排)。

  • 重绘:当我们对 DOM 的修改导致了样式的变化、却并未影响其几何属性(比如修改了颜色或背景色)时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式(跳过了上图所示的回流环节)。这个过程叫做重绘。

重绘不一定导致回流,回流一定会导致重绘。回流比重绘做的事情更多,带来的开销也更大。在开发中,要从代码层面出发,尽可能把回流和重绘的次数最小化。

https://juejin.im/post/5d76222c6fb9a06b0202e926

「点点赞赏,手留余香」

    还没有人赞赏,快来当第一个赞赏的人吧!
Python
0 条回复 A 作者 M 管理员
    所有的伟大,都源于一个勇敢的开始!
欢迎您,新朋友,感谢参与互动!欢迎您 {{author}},您在本站有{{commentsCount}}条评论