HTML 渲染:重要的一课
标题图片:现场笔记和铅笔,作者:helloquence
我在周末发了一条推文:
我想解释一下到底发生了什么,以及我如何设法将页面渲染时间从近 3 秒缩短到 700 毫秒,而无需(这是关键部分)使用任何类型的缓存。
你需要知道的一件事是,我并不擅长前端开发——我只能说,水平一般。这就是为什么这件事(我最终发现的)对我来说有点像一个顿悟的时刻。但这也很合理。
该网站
首先,你需要了解我尝试优化的网站的原始状态。首先,是那些看似不起眼的 HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<link rel="stylesheet" href="dist/css/bootstrap.min.css">
<link rel="stylesheet" href="dist/css/custom.min.css">
</head>
<body>
<main class="page landing-page">
<section class="clean-block clean-hero" id="hero">
<div class="text">
<h2>Jamie "GaProgMan" Taylor<br></h2>
<p>.NET Core Developer; Podcaster; Writer</p>
</div>
</section>
</main>
</body>
</html>
接下来是一些内容custom.css
:
/* other rules, which override
* default bootstrap styles here
*/
#hero {
background-image: url('../img/header.jpg');
color: rgba(0,92,151,0.85);
}
custom.css
被设计为一个覆盖部分 Bootstrap 默认样式的文件,因此必须在 Bootstrap 之后加载。因此,CSS 文件的顺序非常重要。
所有这些创建的内容与以下屏幕截图相匹配:
问题——以及浏览器*如何渲染 HTML
*
= 无论如何,有些浏览器
我接下来要解释浏览器是如何进行抓取、渲染、抓取和重绘的。由于我打算从非常高的角度来解释,所以有些细节可能会有点错误。不过,我的想法基本正确。
因此,当访问页面时,上面的 HMTL 会被下载。浏览器开始解析 HTML,并向链接的 CSS 文件发送请求。你明白我的意思了吗?
因为bootstrap.min.css
是先请求的,所以无论custom.min.css
返回速度是否更快,浏览器都会解析并应用bootstrap.min.css
第一个规则。只有在应用了 bootstrap 之后,浏览器才会应用自定义的样式规则。
这意味着,ID 为“hero”的 div 只有在 bootstrap 下载并应用之后才会background-image
应用该规则。即便如此,浏览器也需要下载图片。所以我们需要等待很长时间才能下载图片。
当我在故意限制网络连接的情况下进行测试时
我的目标是从移动优先的角度设计网站;当我这样做的时候,我也会从移动优先的角度进行测试。这通常意味着我在测试过程中会将网络连接调低到低质量的 3G 连接。
我发现标题图像平均需要 2-3 秒才能加载并显示。
不好。
开发人员要做什么?
我花了一点时间思考我能做什么。我把标题图片缩小了;我用TinyPng之类的工具处理了它;我把图片移到了一台非常快的服务器上;我甚至考虑过使用像CloudFlare这样的缓存反向代理服务。
我研究了使用 Brotli 在服务器上压缩图像(但在某些浏览器上不起作用);我研究了使用srcset
属性;我甚至研究了使用服务器端缓存。
然后我就去吃午饭了。
午饭回来后,我把 bootstrap 删掉了,发现瓶颈在于服务器提供图片的速度。在我的限速连接下,图片加载耗时大约 400 毫秒。
等一下。为什么 bootstrap 会导致这种情况?
事实并非如此。
CSS 文件顺序
还记得我之前说过,我们必须等到 bootstrap 下载并应用完毕后,才能应用“hero”的 background-image 规则吗?好吧,为什么不创建一个单独的 css 文件,只包含这条规则呢?
所以我重写了 HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<link rel="stylesheet" href="dist/css/header.min.css">
<link rel="stylesheet" href="dist/css/bootstrap.min.css">
<link rel="stylesheet" href="dist/css/custom.min.css">
</head>
<body>
<main class="page landing-page">
<section class="clean-block clean-hero" id="hero">
<div class="text">
<h2>Jamie "GaProgMan" Taylor<br></h2>
<p>.NET Core Developer; Podcaster; Writer</p>
</div>
</section>
</main>
</body>
</html>
唯一的规则header.min.css
是:
#hero {
background-image: url('../img/header.jpg');
color: rgba(0,92,151,0.85);
}
这意味着在下载图像之前不需要加载引导程序。
太成功了!页面顶部的英雄横幅和导航大约需要 600 毫秒才能加载完成。由于设计将这些内容(没有其他内容)放在了首屏上方,这意味着我无需担心其余内容的加载时间。
但我们可以做得更好
CSS 文件很棒,而且速度超快。但是浏览器必须再往返一次服务器才能下载背景图片。那么,如何减少这个时间呢?
我们不能假设服务器会支持 http2,这样我们就可以并行向客户端推送更多内容。那么我该怎么办呢?
输入:Base64字符串
对于那些不知道的人来说,Base64是一种编码字符串的方式 - 它绝不安全,但如果您需要通过仅ASCII介质传输二进制数据,它会很有用。
您可以拍摄一张图片,将其编码为 base64 字符串,然后将该字符串用作图片元素属性的值src
。但这种技术确实存在一些缺点,主要有两个:
- 浏览器支持并不完善
- 除非你不关心针对微软浏览器
- 由于图片的 base64 编码长度较长,下载时间可能会更长
- 例如,本文顶部的网站设计截图的 base64 字符串长度为 80,161 个字符
但是,如果将 base64 字符串与 gzip 结合使用,可以获得相当不错的传输速度。因此,我为我的英雄图片生成了一个 base64 字符串,并修改了header.min.css
文件:
#hero {
background-image: url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAUEBAQEAwUEBAQGBQUGCA0ICAcHC /* and the rest of the base64 string here*/ 5c1O5zUrxIMGEJWl6f/2Q=');
color: rgba(0,92,151,0.85);
}
这样就不再需要额外的服务器往返,初始下载时间又减少了 50 毫秒。此外,CSS 下载完成后会立即缓存在浏览器中。
反思
我实际上并不需要采取额外的步骤(使用 base64 字符串),但如果我对你说实话,我只是用它作为一个实验来看看它是否可以更快。
正是这种微优化(将更简单的 CSS 规则移到阻止调用的位置)能够显著提升网站的感知速度。正如我在文章开头所说,我并不是前端开发方面的高手,但我想分享一下。
另外,我的做法可能根本就不算“正确”或优化。如果你是我,你会怎么做?
鏂囩珷鏉ユ簮锛�https://dev.to/dotnetcoreblog/html-rendering-an-important-lesson-54hp