漫谈网站优化提速
前几天的一个晚上,在和一个偶然认识的小白,聊了半个晚上的网站加速的事情,总觉自己最后没有讲清楚,固有此文产生。
本篇文章只涉及前端优化,暂不涉及后端操作,默认后端能抗住所有访问,算力无限大,响应时间无限小。因为加上后端的话,这个命题不是短短的几篇文章搞的定的,大多数都要依据具体的业务来确定。
本文涉及到的浏览器为Chrome浏览器,不具有统一性,仅供参考使用。
用户和网站的交互是通过浏览器来完成的,要谈前端优化,那么,我们就要搞清楚,从用户输入了一串url以后,浏览器到底做了什么。
1. 浏览器如何打开一个网页
这里我们先不考虑路由寻址的事情,后面我们再细细道来,在Chrome浏览器中先打开F12,打开network,可以看到一个网站从输入url到页面显示,具体发送了多少请求。我们以百度为示例,看一下:
首先第一行,可以看到浏览器请求了百度这个页面的主题文件HTML,当浏览器收到这个HTML之后,浏览器和这个页面的缘分,就此开始。
1.1 浏览器渲染流程
用户请求的HTML文本(text/html)通过浏览器的网络层到达渲染引擎后,渲染工作开始。每次通常渲染不会超过8K的数据块,其中基础的渲染流程图:
webkit引擎渲染的详细流程,其他引擎渲染流程稍有不同:
渲染流程有四个主要步骤:
-
解析HTML生成DOM树 - 渲染引擎首先解析HTML文档,生成DOM树
-
构建Render树 - 接下来不管是内联式,外联式还是嵌入式引入的CSS样式会被解析生成CSSOM树,根据DOM树与CSSOM树生成另外一棵用于渲染的树-渲染树(Render tree),
-
布局Render树 - 然后对渲染树的每个节点进行布局处理,确定其在屏幕上的显示位置
-
绘制Render树 - 最后遍历渲染树并用UI后端层将每一个节点绘制出来
以上步骤是一个渐进的过程,为了提高用户体验,渲染引擎试图尽可能快的把结果显示给最终用户。它不会等到所有HTML都被解析完才创建并布局渲染树。它会在从网络层获取文档内容的同时把已经接收到的局部内容先展示出来。
1.2 渲染细节
浏览器渲染一个页面的过程叫做“关键渲染路径(Critical Rendering Path 简称CRP)”,下面我们来聊一下什么是CRP,这对我们进行代码级的优化有很大的指导意义。
- CRP的相关知识对于如何提升网站性能是相当有用的,共有6个步骤:
- 构建DOM树
- 构建CSSOM树
- 运行JavaScript
- 创建渲染树
- 生成布局
- 绘制页面
如下图:
下面详细聊一下什么是CRP,不感兴趣的童鞋可以直接跳过这一段看结论。
1.1.1 构建DOM树
DOM(Document Object Model)树是一个表示整个解析过的HTML页面的对象,从根节点<html>开始,会创建页面中的每个元素/文本节点。
举个栗子:
<html>
<head>
<title>Understanding the Critical Rendering Path</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<h1>Understanding the Critical Rendering Path</h1>
</header>
<main>
<h2>Introduction</h2>
<p>Lorem ipsum dolor sit amet</p>
</main>
<footer>
<small>Copyright 2017</small>
</footer>
</body>
</html>
上面的 HTML 将会被解析成下面的DOM树:
HTML的优点在于它不必等待整个页面加载完成才呈现页面,可以解析一部分,显示一部分。
- 但是像CSS、JavaScript等其他资源会阻止页面渲染。
1.1.2 构建CSSOM树
CSSOM(CSS Object Model) 是一个跟DOM相关的样式对象。它跟DOM的表示方法是相似的,但是不论显式声明还是隐式继承,每个节点都存在关联样式。
在上面提到的html页面的style.css中的样式如下
body { font-size: 18px; }
header { color: plum; }
h1 { font-size: 28px; }
main { color: firebrick; }
h2 { font-size: 20px; }
footer { display: none; }
它会被构建成下面的CSSOM树
CSS 被认为是 “渲染阻塞资源”,它意味着如果不首先完全解析资源,渲染树是无法构建的。CSS由于它的层叠继承的性质,不能像HTML一样解析一部分,显示一部分。定义在文档后面的样式会覆盖或改写之前定义的样式,因为在整个样式表都被解析之前,如果我们使用了在样式表中较早定义的样式,那错误的样式将被应用。这意味着CSS必须被全部解析之后,才能开始下一步。
1.1.3 运行JavaScript
JavaScript被认为是解析阻塞资源,这意味着HTML的解析会被JavaScript阻塞。
当解析器解析到 <script> 标签时,无论该资源是内部还是外链的都会停止解析,先去下载资源。这也是为什么,当页面内有引用JavaScript文件时,引用标签要放到可视元素之后了。
为避免JavaScript解析阻塞,它可以通过设定 async 属性来要求其异步加载。
<script async src="script.js">
1.1.4 创建渲染树
渲染树是DOM和CSSOM的结合体,它代表最终会渲染在页面上的元素的结构对象。这意味着它只关注可见内容,对于被隐藏或者CSS属性 display:none 的属性,不会被包含在结构内。
使用上面例子的DOM和CSSOM,渲染树被创建如下:
1.1.5 生成布局
布局决定了浏览器视窗的大小,它提供了上下文依赖的CSS样式,如百分比或窗口的单位。视窗尺寸通常通过 <head> 标签中的 <meta> 中的 viewport 设定来决定。如果不存在该标签,则通常默认为 980px
例如,最常用的 meta veiwport 的值将会被设置为和设备宽度相符:
<meta name="viewport" content="width=device-width,initial-scale=1">
如果用户访问网页的设备宽度为1000px。然后整体视窗尺寸就会基于这个宽度值了,比如 50% 就是500px, 10vw 就是100px 等等。
1.1.6 绘制页面
最后,在绘制页面步骤。页面上的所有可见内容都会被转换为像素并呈现在屏幕上。
具体的绘制时间跟DOM数以及应用的样式有关。有些样式会花费更多的执行时间,比如复杂的渐变背景图片所需要的计算时间远超过简单固定背景色。
1.2 总结
纵观整个CRP流程,CSS,JS等静态资源加载是阻塞式的,而我们的目标是让整个页面最快呈现在用户面前,并可以使用,而一个页面的呈现和HTML、CSS息息相关,那么这两个一定是要最先被加载的:
- 关键的css需要放在html的头部位置,保证被最先加载,加载完成后可以开始渲染页面。
JS只是关系了网站的一些交互操作,包括一些动态效果,那么:
- js应该放在html的最下部,和渲染视图脱离,保证视图渲染完成后再加载js,同时,js应尽量保持异步加载,不要阻塞主线程。
页面除了HTML,JS,CSS还有一个使用最多的那就是图片
- 图片要做压缩,很多时候高清大图并不是一个好的选择
- 图片尽量使用jpg格式(体积小)
- 图片除了首屏以外,最好使用懒加载的方式,减少初次页面渲染所需要的时间
- 小的icon整合成一张大图,减少图片加载请求数(使用时可以使用css对图片显示做定位)
同时,还可以从网络层考虑优化方向:
- 重新review整个页面,去除所有不必要的资源加载
- css样式表可以合并成为一个文件,减少浏览器请求数
- css样式表需要做压缩,减少网络传输时间(具体有工具,大家可以自己去网上找)
还有很多我一下想不到的地方,不过总体的思想就几句话:
- 尽一切可能减少网络请求数
- 尽一切可能减少网络传输数据
- 如果以上两点都做到了,那么除了关键html和css以外,其他资源的加载请尽量走异步的形式。
2. 从url到浏览器之间到底都经历了什么?
上面我们聊了浏览器接受到资源请求后的一些操作处理,那么从url输入浏览器到浏览器接收到资源中间到底都经历了什么,这一块我们能做什么优化呢?
首先,我们来看一下从url到浏览器之间到底都经历了什么?
- 首先,在浏览器地址栏中输入url
- 浏览器先查看浏览器缓存-系统缓存-路由器缓存,如果缓存中有,会直接在屏幕中显示页面内容。若没有,则跳到第三步操作。
- 在发送http请求前,需要域名解析(DNS解析),解析获取相应的IP地址。
- 浏览器向服务器发起tcp连接,与浏览器建立tcp三次握手。
- 握手成功后,浏览器向服务器发送http请求,请求数据包。
- 服务器处理收到的请求,将数据返回至浏览器
- 浏览器收到HTTP响应
- 读取页面内容,浏览器渲染,解析html源码
- 生成Dom树、解析css样式、js交互
- 客户端和服务器交互
首先,我们能做优化的地方只有可怜的2,包括4、5、6、7、8都是标准的处理流程,我们也无法干涉。
2.1 缓存查找
- 浏览器缓存:浏览器会记录DNS一段时间,因此,只是第一个地方解析DNS请求;
- 操作系统缓存:如果在浏览器缓存中不包含这个记录,则会使系统调用操作系统,获取操作系统的记录(保存最近的DNS查询缓存);
- 路由器缓存:如果上述两个步骤均不能成功获取DNS记录,继续搜索路由器缓存;
- ISP缓存:若上述均失败,继续向ISP搜索。
小结:
- 在很少有改动的页面上开启浏览器缓存,在打开页面时尽可能的利用浏览器缓存
3. 网站访问慢还有什么可能?
网站打开速度慢受很多因素的影响,简单归纳下常见的几个原因:
- 主机服务器不堪重负,响应速度慢;
- 静态资源占用了主机服务大量的带宽,达到了上限;
- 网站的图片和内容太大,需要花费很多时间下载;
- 网站使用了太多不同的脚本和图片,这些脚本和图片没有针对快速加载网站进行优化,加载时间长;
- 网站的服务器位置与终端用户位于不同的地理位置。
其实还有许多其他的原因,但这些以上列举的几点是比较常见的。
3.1 终极大杀器——CDN
3.1.1 什么是CDN?
CDN指的是内容分发网络。其基本思路是尽可能的避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。
通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。其目的是使用户可就近取得所需内容,解决Internet网络拥挤的状况,提高用户访问网站的响应速度。
简单打个比方,我们常喜欢在京东上买东西,今天下单明天就能送到。而在淘宝上,我们享受不到这样的速度。为什么呢?因为京东的物流体系完善。假设你在上海购买了海南的一件商品,淘宝走快递可能要走3天才到你手上,但是京东在全国设有仓库物流点,从就近的杭州发货点发货到上海,一天就可以到。
而CDN一般都由运营商或者大型云服务提供商来提供,可以给更大的网络带宽,可以部署的离终端用户位置更近,有智能DNS,有效缓解所有流量全部回源对我们的主服务造成的流量冲击,带来更高的可用性,当然,这样会造成静态资源缓存,如果我们需要更新静态资源,面临着缓存的挑战,不过,也不是没有解决方案的,所有的静态资源在访问的时候都应该加上版本号,防止访问到前面版本的资源,造成更新不及时。
参考: