热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

浏览器衬着机制

本文示例源代码请戳github博客,提议人人着手敲敲代码。媒介浏览器衬着页面的历程从耗时的角度,浏览器要求、加载、衬着一个页面,时刻花在下面五件事变上:DNS查询TCP衔接HTTP

本文示例源代码请戳
github博客,提议人人着手敲敲代码。

媒介

浏览器衬着页面的历程

从耗时的角度,浏览器要求、加载、衬着一个页面,时刻花在下面五件事变上:

  1. DNS 查询
  2. TCP 衔接
  3. HTTP 要求即响应
  4. 服务器响应
  5. 客户端衬着

本文议论第五个部份,即浏览器对内容的衬着,这一部份(衬着树构建、规划及绘制),又能够分为下面五个步骤:

  1. 处置惩罚 HTML 标记并构建 DOM 树。
  2. 处置惩罚 CSS 标记并构建 CSSOM 树
  3. 将 DOM 与 CSSOM 兼并成一个衬着树。
  4. 依据衬着树来规划,以盘算每一个节点的多少信息。
  5. 将各个节点绘制到屏幕上。

须要邃晓,这五个步骤并不一定一次性递次完成。假如 DOM 或 CSSOM 被修正,以上历程须要反复实行,如许才盘算出哪些像素须要在屏幕上举行从新衬着。现实页面中,CSS 与 Javascript 往往会屡次修正 DOM 和 CSSOM。

1、浏览器的线程

在细致申明之前我们来看一下浏览器线程。这将有助于我们明白后续内容。

浏览器是多线程的,它们在内核制控下相互配合以坚持同步。一个浏览器最少完成三个常驻线程:Javascript 引擎线程,GUI 衬着线程,浏览器事宜触发线程。

  • GUI 衬着线程:担任衬着浏览器界面 HTML 元素,当界面须要重绘(Repaint)或由于某种操纵激发回流(reflow)时,该线程就会实行。在 Javascript 引擎运转剧本时期,GUI 衬着线程都是处于挂起状况的,也就是说被”凝结”了。
  • Javascript 引擎线程:重要担任处置惩罚 Javascript 剧本顺序。
  • 定时器触发线程:浏览器定时计数器并非由 Javascript 引擎计数的, Javascript 引擎是单线程的, 假如处于壅塞线程状况就会影响记计时的正确, 因而浏览器经由过程零丁线程来计时并触发定时。
  • 事宜触发线程:当一个事宜被触发时该线程会把事宜添加到待处置惩罚行列的队尾,守候 JS 引擎的处置惩罚。这些事宜包含当前实行的代码块如定时使命、浏览器内核的其他线程如鼠标点击、AJAX 异步要求等。由于 JS 的单线程关联一切这些事宜都得列队守候 JS 引擎处置惩罚。定时块任何和 ajax 要求等这些异步使命,事宜触发线程只是在抵达定时时刻或许是 ajax 要求胜利后,把回调函数放到事宜行列当中。
  • 异步 HTTP 要求线程:在 XMLHttpRequest 在衔接后是经由过程浏览器新开一个线程要求, 将检测到状况变动时,假如设置有回调函数,异步线程就发生状况变动事宜放到 Javascript 引擎的处置惩罚行列中守候处置惩罚。在提议了一个异步要求时,http 要求线程则担任去要求服务器,有了响应今后,事宜触发线程再把回到函数放到事宜行列当中。
2、构建DOM树与CSSOM树

浏览器从收集或硬盘中取得HTML字节数据后会经由一个流程将字节剖析为DOM树:

  • 编码: 先将HTML的原始字节数据转换为文件指定编码的字符。
  • 令牌化: 然后浏览器会依据HTML范例来将字符串转换成种种令牌(如如许的标签以及标签中的字符串和属性等都邑被转化为令牌,每一个令牌具有迥殊寄义和一组划定规矩)。令牌记录了标签的最先与终了,经由过程这个特征能够轻松推断一个标签是不是为子标签(假设有两个标签,当标签的令牌还未碰到它的终了令牌就碰见了标签令牌,那末就是的子标签)。
  • 天生对象: 接下来每一个令牌都邑被转换成定义其属性和划定规矩的对象(这个对象就是节点对象)
  • 构建终了: DOM树构建完成,全部对象鸠合就像是一棵树形构造。能够有人会迷惑为何DOM是一个树形构造,这是由于标签之间含有庞杂的父子关联,树形构造恰好能够诠释这个关联(CSSOS同理,层叠款式也含有父子关联。比方: div p {font-size: 18px},会先寻觅一切p标签并推断它的父标签是不是为div以后才会决议要不要采纳这个款式举行衬着)。

全部DOM树的构建历程实在就是: 字节 -> 字符 -> 令牌 -> 节点对象 -> 对象模子,
下面将经由过程一个示例HTML代码与配图更抽象地诠释这个历程。








Hello web performance students!




《浏览器衬着机制》

当上述HTML代码碰见标签时,浏览器会发送要求取得该标签中标记的CSS文件(运用内联CSS能够省略要求的步骤进步速率,但没有必要为了这点速率而丧失了模块化与可维护性),style.css中的内容以下:

body { font-size: 16px }
p { font-weight: bold }
span { color: red }
p span { display: none }
img { float: right }

浏览器取得外部CSS文件的数据后,就会像构建DOM树一样最先构建CSSOM树,这个历程没有什么迥殊的差异。
《浏览器衬着机制》

3、构建衬着树

在构建了DOM树和CSSOM树以后,浏览器只是具有了两个相互自力的对象鸠合,DOM树形貌了文档的构造与内容,CSSOM树则形貌了对文档运用的款式划定规矩,想要衬着出页面,就须要将DOM树与CSSOM树连系在一同,这就是衬着树。
《浏览器衬着机制》

  • 浏览器会先从DOM树的根节点最先遍历每一个可见节点(不可见的节点天然就没必要衬着到页面了,不可见的节点还包含被CSS设置了display: none属性的节点,值得注重的是visibility: hidden属性并不算是不可见属性,它的语义是隐蔽元素,但元素依旧占有着规划空间,所以它会被衬着成一个空框)
  • 对每一个可见节点,找到其适配的CSS款式划定规矩并运用。
  • 衬着树构建完成,每一个节点都是可见节点而且都含有其内容和对应划定规矩的款式。
4、规划与绘制

CSS采纳了一种叫做盒子模子的头脑模子来示意每一个节点与其他元素之间的间隔,盒子模子包含外边距(Margin),内边距(Padding),边框(Border),内容(Content)。页面中的每一个标签实在都是一个个盒子

《浏览器衬着机制》
规划阶段会从衬着树的根节点最先遍历,然后肯定每一个节点对象在页面上的确实大小与位置,规划阶段的输出是一个盒子模子,它会精确地捕捉每一个元素在屏幕内的确实位置与大小,一切相对的测量值也都邑被转换为屏幕内的相对像素值。








Hello world!



《浏览器衬着机制》

当Layout规划事宜完成后,浏览器会马上发出Paint Setup与Paint事宜,最先将衬着树绘制成像素,绘制所需的时刻跟CSS款式的庞杂度成正比,绘制完成后,用户就能够看到页面的终究显现结果了。

我们对一个网页发送要求并取得衬着后的页面能够也就经由了1~2秒,但浏览器实在已做了上述所讲的异常多的事情,总结一下浏览器症结衬着途径的全部历程:

  • 处置惩罚HTML标记数据并天生DOM树。
  • 处置惩罚CSS标记数据并天生CSSOM树。
  • 将DOM树与CSSOM树兼并在一同天生衬着树。
  • 遍历衬着树最先规划,盘算每一个节点的位置信息。
  • 将每一个节点绘制到屏幕。
5、外部资本是怎样要求的

为了直观的视察浏览器加载和衬着的细节,当地用nodejs搭建一个简朴的HTTP Server。
index.js

const http = require('http');
const fs = require('fs');
const hostname = '127.0.0.1';
const port = 8080;
http.createServer((req, res) => {
if (req.url == '/a.js') {
fs.readFile('a.js', 'utf-8', function (err, data) {
res.writeHead(200, {'Content-Type': 'text/plain'});
setTimeout(function () {
res.write(data);
res.end()
}, 5000)
})
} else if (req.url == '/b.js') {
fs.readFile('b.js', 'utf-8', function (err, data) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.write(data);
res.end()
})
} else if (req.url == '/style.css') {
fs.readFile('style.css', 'utf-8', function (err, data) {
res.writeHead(200, {'Content-Type': 'text/css'});
res.write(data);
res.end()
})
} else if (req.url == '/index.html') {
fs.readFile('index.html', 'utf-8', function (err, data) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(data);
res.end()
})
}
}).listen(port, hostname, () => {
console.log('Server running at ' + hostname + ':' + port);
});

index.html









没有 defer 或 async,浏览器会马上加载并实行指定的剧本,也就是说不守候后续载入的文档元素,读到就加载并实行。

  • 状况2 (异步下载)

async 属性示意异步实行引入的 Javascript,与 defer 的区分在于,假如已加载好,就会最先实行——不管现在是 HTML 剖析阶段照样 DOMContentLoaded 触发以后。须要注重的是,这类体式格局加载的 Javascript 依旧会壅塞 load 事宜。换句话说,async-script 能够在 DOMContentLoaded 触发之前或以后实行,但一定在 load 触发之前实行。

  • 状况3 (耽误实行)

defer 属性示意耽误实行引入的 Javascript,即这段 Javascript 加载时 HTML 并未住手剖析,这两个历程是并行的。全部 document 剖析终了且 defer-script 也加载完成以后(这两件事变的递次无关),会实行一切由 defer-script 加载的 Javascript 代码,然后触发 DOMContentLoaded 事宜。

defer 与比拟一般 script,有两点区分:

  • 载入 Javascript 文件时不壅塞 HTML 的剖析,实行阶段被放到 HTML 标签剖析完成以后。
  • 在加载多个JS剧本的时刻,async是无递次的加载,而defer是有递次的加载。
8、css文件的影响

服务端将style.css的响应也设置耽误。

fs.readFile('style.css', 'utf-8', function (err, data) {
res.writeHead(200, {'Content-Type': 'text/css'});
setTimeout(function () {
res.write(data);
res.end()
}, 5000)
})












222222


3333333






能够看出来,css文件不会壅塞HTML剖析,然则会壅塞衬着,致使css文件未下载完成之前已剖析好html也没法先显现出来。

我们把css调解到尾部











222222


3333333






这是页面能够衬着了,然则没有款式。直到css加载完成

以上我们能够简朴总结。

  • CSS 放在 head 中会壅塞页面的衬着(页面的衬着会比及 css 加载完成)
  • CSS 壅塞 JS 的实行 (由于 GUI 线程和 JS 线程是互斥的,由于有能够 JS 会操纵 CSS)
  • CSS 不壅塞外部剧本的加载(不壅塞 JS 的加载,但壅塞 JS 的实行,由于浏览器都邑有预先扫描器)

参考
浏览器衬着历程与机能优化
聊聊浏览器的衬着机制
你不知道的浏览器页面衬着机制


推荐阅读
  • 实践指南:使用Express、Create React App与MongoDB搭建React开发环境
    本文详细介绍了如何利用Express、Create React App和MongoDB构建一个高效的React应用开发环境,旨在为开发者提供一套完整的解决方案,包括环境搭建、数据模拟及前后端交互。 ... [详细]
  • Python3爬虫入门:pyspider的基本使用[python爬虫入门]
    Python学习网有大量免费的Python入门教程,欢迎大家来学习。本文主要通过爬取去哪儿网的旅游攻略来给大家介绍pyspid ... [详细]
  • 深入理解:AJAX学习指南
    本文详细探讨了AJAX的基本概念、工作原理及其在现代Web开发中的应用,旨在为初学者提供全面的学习资料。 ... [详细]
  • 本文提供了一个详尽的前端开发资源列表,涵盖了从基础入门到高级应用的各个方面,包括HTML5、CSS3、JavaScript框架及库、移动开发、API接口、工具与插件等。 ... [详细]
  • 本文探讨了如何通过优化 DOM 操作来提升 JavaScript 的性能,包括使用 `createElement` 函数、动画元素、理解重绘事件及处理鼠标滚动事件等关键主题。 ... [详细]
  • 本文由公众号【数智物语】(ID: decision_engine)发布,关注获取更多干货。文章探讨了从数据收集到清洗、建模及可视化的全过程,介绍了41款实用工具,旨在帮助数据科学家和分析师提升工作效率。 ... [详细]
  • 本文介绍了如何通过安装和配置php_uploadprogress扩展来实现文件上传时的进度条显示功能。通过一个简单的示例,详细解释了从安装扩展到编写具体代码的全过程。 ... [详细]
  • 页面预渲染适用于主要包含静态内容的页面。对于依赖大量API调用的动态页面,建议采用SSR(服务器端渲染),如Nuxt等框架。更多优化策略可参见:https://github.com/HaoChuan9421/vue-cli3-optimization ... [详细]
  • 本文详细介绍如何在SSM(Spring + Spring MVC + MyBatis)框架中实现分页功能。包括分页的基本概念、数据准备、前端分页栏的设计与实现、后端分页逻辑的编写以及最终的测试步骤。 ... [详细]
  • 本文探讨了使用Python实现监控信息收集的方法,涵盖从基础的日志记录到复杂的系统运维解决方案,旨在帮助开发者和运维人员提升工作效率。 ... [详细]
  • 本文回顾了作者在求职阿里和腾讯实习生过程中,从最初的迷茫到最后成功获得Offer的心路历程。文中不仅分享了个人的面试经历,还提供了宝贵的面试准备建议和技巧。 ... [详细]
  • 本文详细介绍了JQuery Mobile框架中特有的事件和方法,帮助开发者更好地理解和应用这些特性,提升移动Web开发的效率。 ... [详细]
  • 理解浏览器历史记录(2)hashchange、pushState
    阅读目录1.hashchange2.pushState本文也是一篇基础文章。继上文之后,本打算去研究pushState,偶然在一些信息中发现了锚点变 ... [详细]
  • 汇编语言标识符和表达式(四)(表达式与符号定义语句)
    7、表达式表达式是程序设计课程里的一个重要的基本概念,它可由运算符、操作符、括号、常量和一些符号连在一起的式子。在汇编语言中,表达式分为:数值表达式和地址表达式。(1)进制伪指令R ... [详细]
  • C/C++ 应用程序的安装与卸载解决方案
    本文介绍了如何使用Inno Setup来创建C/C++应用程序的安装程序,包括自动检测并安装所需的运行库,确保应用能够顺利安装和卸载。 ... [详细]
author-avatar
帅哥大香蕉
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有