浏览器基础知识
通过浏览器访问页面,首先浏览器会请求回来一个HTML文档,然后自上而下开始渲染,这里面就包括进程和线程的基本概念
- 进程: 指一个程序 ( 浏览器打开一个页面,就相当于开一个进行 )
- 线程: 进程中具体执行事物的东西,一个线程同时只能做一件事
- 同步编程: 一般是指一个线程去处理任务,上面的任务处理不完,下面的任务就无法处理
- 异步编程:
- 多线程异步编程
- 单线程异步编程 ( Js是EventQueue + EventLoop 完成单线程异步编程的 )
浏览器中的主要线程
通过windows上的任务管理器可以看出,浏览器是可以开辟多进程和多线程的
浏览器的线程主要包括
- GUI 渲染线程:渲染页面
- JS 引擎线程:执行JS代码
- HTTP 网络线程,可以开辟N个网络线程: 从服务器中获取资源和数据
- 定时器监听线程
- DOM监听线程
- ….
页面渲染过程
- 请求一个HTML文件,自上而下读取
- CSS部分
- 如果遇到style内嵌样式,GUI会直接渲染(同步)
- 如果CSS代码较少,可以直接内嵌,拉取HTML的同时CSS也能获取到,直接参与渲染
- 如果CSS代码较多,还通过内嵌分方式,一方面会影响HTML的获取速度,也不利于代码的维护 ;此时推荐外链的方式
- 遇到 link 浏览器会开辟一个HTTP线程去请求资源文件,同时GUI线程会继续执行(异步)
- 浏览器同时发送HTTP请求是有限制的;超过最大并发数其他的请求会暂时挂起
- 遇到 @import ,浏览器也会开辟HTTP线程去拉去资源,但此时的GUI会暂停(导入样式会阻塞GUI的渲染),当资源请求回来, GUI才会继续
- 真实项目中应该避免使用 @import
- 如果遇到style内嵌样式,GUI会直接渲染(同步)
- JS部分
- 遇到<script src=””> 会阻碍GUI的渲染, 同时开辟HTTP线程去获取资源,请求结束运行JS引擎线程, 最后GUI才会渲染
- 可以使用
window.addEventListener("load",callBack)
,等待页面所有资源都加载完,在执行 - 可以使用
window.addEventListener("DOMContentLoaded",callBack)
,等待DOM树渲染完执行 - <script src=”” aync/defer>
- defer : 和 link 是类似的机制, 不会阻碍 GUI 的渲染,无论是HTTP线程还是JS线程,等GUI渲染完, 才会进行JS渲染线程
- async : GUI渲染–单独开辟HTTP线程请求 ( 请求过程GUI不会停止,一旦请求结束会立即执行JS线程,同事终端GUI线程;等到JS线程完成后才会继续GUI线程 )
- 问题: 如果 JS 引擎执行,GUI 没有完全渲染完成, 会存在部分 DOM 节点获取不到的情况
- 可以使用
- 注意: 如果多个 script请求 在没有任何属性方式下是顺序执行的; 如果属性是async,最先返回的先执行(并行);如果是 defer 浏览器内部会在 GUI 渲染完成后,等待所有设置 defer 的资源请求回来,在按照代码的顺序加载JS
- 遇到<script src=””> 会阻碍GUI的渲染, 同时开辟HTTP线程去获取资源,请求结束运行JS引擎线程, 最后GUI才会渲染
总结
在真实项目开发中,我们一般把 link 放在页面的头部 【为了在没有 DOM 的时候,就通知HTTP去请求CSS,这样等DOM渲染完,CSS的资源也获取多了,更有效的安排时间,提高页面渲染效率】;把JS放在页面的底部,防止阻碍GUI的渲染,如果放在顶部,推荐设置 async、defer。
浏览器渲染过程
DOM树(渲染完成后触发 DOMCOntentLoaded 事件 -> [ 执行JS?如果有的话 ] -> CSSOM树 -> 渲染树(浏览器未来是按照这个树绘制的页面)-> Layout布局计算(重排)-> painting绘制(重绘)
关于重绘和重排
- 页面第一次渲染必然会引发一次重排和重绘
- 如果改变元素的大小和位置,浏览器也会进行重排;重排一定会触发重绘
- 如果只是一些普通样式的更改例如color,只需要重绘即可
关于重排的优化方案(CRP优化)
- 减少 DOM 的重排
- 标签语义化和避免深层次嵌套
- CSS 选择器是从右到左,比如:.box a{} 的性能要低于 a{} ;
- 上述特点得出 less \ scss的预处理嵌套样式相对较差(阿里官方吐槽)
- 对 JS 动态添加元素时采用代码片段的方式,一次性添加多个元素
- 动画效果相对频繁的应用到 position 属性为 absolute或者fixed 的元素上(脱离文档流)
- 使用 transform \ opacity \ filters 不会引发重排和重绘,但使用过多会占用大量内存,优势会导致字体模糊
- 设置样式和获取样式分开(读写分离)
- 在新版浏览器中存在 渲染队列机制,如果在设置元素过程中进行样式的读取会拆信一次渲染队列(会引发重排和重绘)
let box = document.querySelector("#box");
box.style.width = "100px"
console.log(style.width) // 此时的的读取出会刷新渲染队列.浏览器进行一次重排
box.style.height = "100px" // 然后会进行第二次重排
上面一段代码,分别改变了 box 元的 width 和 height 属性,在渲染队列机制中对所有的更改机型队列执行,最后完成一次渲染,但在过程中如果出现读取元素,渲染队列会强制刷新,以确保获取的元素属性是目前最新的状态,这样以来针对 height 的更改触发队列中的第二次渲染。所以优化方法就是尽可能将操作和读取元素区分来写,即读写分离。
let box = document.querySelector("#box");
box.style.width = "100px"
box.style.height = "100px"
console.log(style.width)
读写分离的好处在于,不打断渲染队列的情况,让浏览器针对于本次队列的内容统一渲染一次到页面,从而避免了重复触发重排,提高页面渲染性能
本片文章借鉴《珠峰前端高级体系课程》,如有侵权行为,请联系作者删除