热门标签 | 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 的实行,由于浏览器都邑有预先扫描器)

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


推荐阅读
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • 微信小程序导航跟随的实现方法
    本文介绍了在微信小程序中实现导航跟随的方法。通过设置导航的position属性和绑定滚动事件,可以实现页面向下滚动到导航位置时,导航固定在页面最上方;页面向上滚动到导航位置时,导航恢复到原始位置;点击导航可以平滑跳转到相应位置。代码示例也给出了具体实现方法。 ... [详细]
  • SmartRefreshLayout自定义头部刷新和底部加载
    1.添加依赖implementation‘com.scwang.smartrefresh:SmartRefreshLayout:1.0.3’implementation‘com.s ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讲述了如何通过代码在Android中更改Recycler视图项的背景颜色。通过在onBindViewHolder方法中设置条件判断,可以实现根据条件改变背景颜色的效果。同时,还介绍了如何修改底部边框颜色以及提供了RecyclerView Fragment layout.xml和项目布局文件的示例代码。 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • FeatureRequestIsyourfeaturerequestrelatedtoaproblem?Please ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • python3 nmap函数简介及使用方法
    本文介绍了python3 nmap函数的简介及使用方法,python-nmap是一个使用nmap进行端口扫描的python库,它可以生成nmap扫描报告,并帮助系统管理员进行自动化扫描任务和生成报告。同时,它也支持nmap脚本输出。文章详细介绍了python-nmap的几个py文件的功能和用途,包括__init__.py、nmap.py和test.py。__init__.py主要导入基本信息,nmap.py用于调用nmap的功能进行扫描,test.py用于测试是否可以利用nmap的扫描功能。 ... [详细]
  • 带添加按钮的GridView,item的删除事件
    先上图片效果;gridView无数据时显示添加按钮,有数据时,第一格显示添加按钮,后面显示数据:布局文件:addr_manage.xml<?xmlve ... [详细]
  • 本文介绍了JavaScript进化到TypeScript的历史和背景,解释了TypeScript相对于JavaScript的优势和特点。作者分享了自己对TypeScript的观察和认识,并提到了在项目开发中使用TypeScript的好处。最后,作者表示对TypeScript进行尝试和探索的态度。 ... [详细]
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社区 版权所有