第12章 页面加载渲染和性能优化(上)【这些会了,你就可以飞了】

返回章节目录

目录

1.页面加载和渲染过程

页面加载过程

渲染过程(1)

渲染过程(2)

构建DOM树

构建CSSOM树

运行JavaScript

创建Render树

生成布局

绘制(Painting)

把它们结合起来

window.onload和DOMContentLoaded 


由于篇幅原因,我决定将第12章分成上下两部分。。。

1.页面加载和渲染过程

一般来说,经常会被问到一个问题:在浏览器地址栏输入URL,按下回车后究竟发生了什么?

这个问题没有标准答案,里面的知识点足够写出一本书,甚至一本还不够。就看你应聘对应的职位是什么,那么这个问题的答案就会有所偏向。接下来我们来看看就前端而言需要掌握哪些

页面加载过程

一般来说,页面资源分为3类

html代码

媒体文件,如图片、视频等

javascript、css

页面加载基础知识解释:

1.DNS解析: 域名-->IP地址

为什么要域名?仅仅是因为 IP 不好记吗?

不是的,同一个域名对应的IP在不同的区域是不一样的,因为大型网站做分区域的IP均衡代理。你在北京访问百度和你在深圳访问百度的域名是一样的都是baidu.com,但是DNS解析出来的 IP 是不一样的,如果你在北京直接访问深圳的 IP 就会慢很多

在浏览器访问网站的时候,实际还是访问 IP,域名解析服务(DNS)会根据地域去解析成不同的 IP 让你的网站访问的更快

综上所述,使用域名不仅仅是因为容易记住,而且是网页访问更快,因为DNS会解析出来的 IP 距离你很近。

2.浏览器根据 IP 地址向服务器发起http请求

浏览器只是发起方,真正的核心模块还是操作系统,操作系统里有一些可以发起网络请求的服务,调用操作系统的服务,然后操作系统去发起http请求。这里面还有建立连接的三次握手过程,这个可以自行搜索。

3.服务器处理http请求,并返回给浏览器

至于服务器怎么处理http请求这里不讲,返回的页面资源就可能有html代码,媒体文件,如图片、视频等,javascript、css等等。

渲染过程(1)

请求的是页面返回html代码

1.根据HTML代码生成DOM Tree(文本代码生成树结构)

2.根据CSS生成CSSOM

3.将DOM Tree和CSSOM整合形成Render Tree(渲染树)

只根据DOM Tree是无法渲染的,其标签的CSS属性是在CSSOM Tree里面的,可以将Render Tree理解为挂着CSS属性的DOM Tree

渲染过程(2)

1.根据Render Tree渲染页面

2.遇到<script>则暂停渲染,优先加载并执行JS代码,完成再继续

(JS操作和渲染页面操作是共用一个线程的,因为JS可能改变DOM结构而改变Render Tree的结构,所以遇到<script>就暂停渲染,否则渲染了可能没用,Render Tree变了)

3.直到把Render Tree渲染完成

对于渲染感兴趣的,请参见下面大佬的文章,这是我从付费文章专栏copy过来的,为了看所有文章花了我199大洋开年会。

接下来讲的理解关键渲染路径的内容来自大漠老师的收费文章,每篇文章3.99元,在这里免费送给大家学习。

当浏览器从服务器接收到一个HTML页面的请求时,到屏幕上渲染出来要经过很多个步骤。浏览器完成这一系列的运行,或者说渲染出来我们常常称之为“关键渲染路径”(Critical Rendering Path)。

理解CRP(Critical Rendering Path)相关的知识可以更好的提高网站的性能。那么理解我们从下面六个部分来理解CRP相关的知识:

构建DOM树

DOM(文档对象模型)树是一个完全解析的HTML页面对象。从<html>根元素开始到页面中每个元素和文本的节点。元素嵌套在其他元素内则表示为子节点,每个节点包含完整的属性元素。例如一个<a>元素,它就有与之相关的href节点。

来看下面这个简单的DOM示例:

<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>  

这将会创建一个像下面这样的DOM树:

HTML比较好的是它可以执行部分。完整的文档不需要加载的内容会在页面的开始呈现。然而,比如CSS和JavaScript可以阻止页面的呈现。

构建CSSOM树

CSSOM(CSS对象模型)是一个表示DOM样式的对象。它类似于DOM,但是是每个节点相关的样式,包括他们是否显式声明或隐式继承。

比如,在style.css文件中对上面的DOM有这样的一些样式:

body { font-size: 18px; }

header { color: plum; }  
h1 { font-size: 28px; }

main { color: firebrick; }  
h2 { font-size: 20px; }

footer { display: none; }  

这将会构建像下面这样的一个CSSOM树:

CSS被认为是“渲染阻塞资源”。这意味着渲染树(见下文)的构建离不开延续一个资源的解析完成度。不像HTML,CSS不能只用部分,因为CSS是具有继承层叠特性。文档后面定义的样式可以覆盖前面定义的样式。所以,如果我们开始使用CSS时,之前的样式表会被认为已经全部解析完。这也意味着CSS必须充分解析才能继续下一个阶段。

如果只运用于当前设备,CSS文件只被认为阻塞。<link rel="stylesheet">标签可以接受一个media属性,可以用来指定样式适用于何种媒体。例如,我们有一个样式表,它的media属性设置为orientation:landscape时,如果在portrait模式下查看页面,那么这个样式表不会被认为是一个阻塞资源。

CSS还会阻塞JavaScript。那是因为JavaScript文件必须要等CSSOM构建完才会执行。

运行JavaScript

JavaScript被认为是一个“解析器阻塞资源”。这意味着解析的HTML文档本身就会被JavaScript阻塞。

当解析器读到<script>标记,不管是内部的还是外部的,它停止获取(如果是外部的)并运行它。这个为什么,如果我们有一个JavaScript文件,该文件引用文档中的元素,那么它必须放在这个元素的后面。

为了避免JavaScript解析器造成阻塞,可以添加async属性,异步加载它。

<script async src="script.js">  

创建Render树

Render树是DOM和CSSOM的组合。这个树代表最终在页面上呈现的东西。这意味着它只抓住了可见的内容,将不包括设置了hidden的元素和CSS设置了display:none的元素。

使用上面的DOM和CSSOM,构建的Render树如下图所示:

生成布局

布局根据CSS样式设置的大小来决定页面在窗口中的尺寸。视窗的大小是由<head>viewport标记来决定,如果没有提供这个标记,默认使用的视窗宽度是980px

例如,常见的设置视窗的meta标签,指定的大小对应设备宽度:

<meta name="viewport" content="width=device-width,initial-scale=1">  

如果用户访问页面宽度是基于设备的宽度1000px。那一半的视窗就是500px10vw就是100px

绘制(Painting)

最后就是绘制(Painting)步骤,把页面可见的内容转化为像素在屏幕上显示。

绘制需要多少时间这取决于DOM的大小以及应用的样式。有一些样式需要更多的执行时间。例如,一个复杂的渐变背景图像比一个单一颜色背景绘制需要更多的时间。

把它们结合起来

我们可以在DevTools中看到关键渲染路径的过程。在Chrome浏览器中,点击Timeline(现在是Performance)选项。

拿文章前面的示例(这里添加了<script>标签):

<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>
  <script src="main.js"></script>
</body>  
</html>  

如果我们看页面的加载日志,将看到如下这样的数据:

  • 发送请求:发送GET,请求index.html
  • 解析HTML并发送请求:解析HTML并且构建DOM树,发送GET请求,请求style.cssmain.js
  • 解析样式:根据style.css创建CSSOM
  • 脚本评估main.js评估
  • 布局:基于HTML中的视窗meta生成布局
  • 绘制:绘制页面

如果觉得不过瘾,我们继续来看看《深入浅出Vue.js》的作者刘博文写的一篇文章关键渲染路径

window.onload和DOMContentLoaded 

如果在渲染的时候遇到img标签链接的图片还没下载完也会继续渲染,会把这个位置空出来,等到图片下载完成插入就行,图片的大小未指定可能引起reflow,比如图片过大把旁边内容撑下去了,所以最好图片设置宽和高来避免重新渲染。

下面我们来看一个例子演示load和DOMContentLoaded执行时机

index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>运行环境 演示</title>
    </head>
    <body>
        <p>一段文字 1</p>
        <p>一段文字 2</p>
        <p>一段文字 3</p>
        <img
            id="img1"
            src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1570191150419&di=37b1892665fc74806306ce7f9c3f1971&imgtype=0&src=http%3A%2F%2Fimg.pconline.com.cn%2Fimages%2Fupload%2Fupc%2Ftx%2Fitbbs%2F1411%2F13%2Fc14%2F26229_1415883419758.jpg"
        />

        <script src="./index.js"></script>
    </body>
</html>

index.js

const img1 = document.getElementById('img1')
img1.onload = function () {
    console.log('img loaded')
}

window.addEventListener('load', function () {
    console.log('window loaded')
})

document.addEventListener('DOMContentLoaded', function () {
    console.log('dom content loaded')
})

控制台的打印依次是

dom content loaded
img loaded
window loaded

就算是我Throttling是设置的Slow 3G,但是仍然等网页加载出来的时候控制台嗖的一下就结束了,现在的浏览器这么屌了?

所以只能在数值上给大家一个直观的感受,下面是Slow 3G的结果,而online的结果DOMContentLoaded比img1的回调早了1.2ms,img1的onload比window的load回调早了0.3ms。

根据我Performance里面的数据

第一个Event是DOMContentLoaded的事件

第二个Event是img1的事件

第三个Event是window的load事件

根据结果显示,Function Call的时候我们可以看到DOMContentLoaded和window的load调用的是匿名函数anonymous,而img1.οnlοad=funtion(){...}是我们指定的onload函数,这和代码也能对应的上

注意,DOMContentLoaded是包含在ParseHTML中的

而ParseHTML结束后才会有img1的onload和window的load

关注、留言,我们一起学习。

===============Talk is cheap, show me the code================

发布了231 篇原创文章 · 获赞 1037 · 访问量 92万+

猜你喜欢

转载自blog.csdn.net/qq_34115899/article/details/104208761