文章目录
1. 浏览器渲染页面
浏览器渲染页面的过程:
- DNS 查询
- TCP 连接
- HTTP 请求即响应
- 服务器响应
- 客户端渲染
客户端渲染【浏览器对内容的渲染】进程如下:
2. 渲染树
当我们生成 DOM 树和 CSSOM 树以后,就需要将这两棵树组合为渲染树。
渲染树只会包括需要显示的节点和这些节点的样式信息,如果某个节点是 display: none 的,那么就不会在渲染树中显示。
工作流程:构建 DOM -> 构建 CSSOM -> 构建渲染树 -> 布局 -> 绘制
处理 HTML 标记并构建 DOM 树。
处理 CSS 标记并构建 CSSOM 树。
将 DOM 与 CSSOM 合并成一个渲染树。
根据渲染树来布局,以计算每个节点的几何信息。
将各个节点绘制到屏幕上。
3. DOM和CSSOM的具体构建流程
DOM 和 CSSOM 都是以" Bytes → characters → tokens → nodes → object model. " 这样的方式生成最终的数据
- 当服务器返回一个HTML文件给浏览器的时候,浏览器接受到的是一些字节数据;
- 使用
<meta http-equiv="content-type"content="text/html;charset=utf-8">
来告诉浏览器页面使用的是什么编码; - 使用这些语义块(token)创建对象,形成一个个节点;
- HTML解析器就会从HTML文件的头部到尾部,一个个地遍历这些节点。普通节点加入到DOM树中,节点是JS代码则交给JS解析器,节点是CSS代码则交给CSS解析器;
- 当HTML解析器读到最后一个节点的时候,整个DOM树也构建完成了,这个时候就会触发domContentloaded事件。
注: DOM树要小,CSS尽量用id和class,千万不要过渡层叠下去,CSS的加载速度与构建CSSOM的速度将直接影响首屏渲染速度
4. 阻塞渲染:CSS 与 JavaScript
- JS 文件不只是阻塞 DOM 的构建,它会导致 CSSOM 也阻塞 DOM 的构建
-
当浏览器遇到一个 script 标记时,DOM 构建将暂停,直至脚本完成执行。
-
JavaScript 可以查询和修改 DOM 与 CSSOM。
-
CSSOM 构建时,JavaScript 执行将暂停,直至 CSSOM 就绪。
CSS 优先:引入顺序上,CSS 资源先于 JavaScript 资源。
5. 改变阻塞模式:async(异步下载) 和 defer(延迟执行)
蓝色线代表 JavaScript 加载;红色线代表 JavaScript 执行;绿色线代表 HTML 解析。
蓝色线代表网络读取,红色线代表执行时间,这俩都是针对脚本的;绿色线代表 HTML 解析
- 情况 1:
<script src="script.js"></script>
没有 defer 或 async,浏览器会立即加载并执行指定的脚本,也就是说不等待后续载入的文档元素,读到就加载并执行。
- 情况 2:
<script async src="script.js"></script>
(异步下载)
async ,与 defer 的区别在于,如果已经加载好,就会开始执行,无论此刻是 HTML 解析阶段还是 DOMContentLoaded 触发之后。
这种方式加载的 JavaScript 依然会阻塞 load 事件。换句话说,async-script 可能在 DOMContentLoaded 触发之前或之后执行,但一定在 load 触发之前执行。
- 情况 3:
<script defer src="script.js"></script>
(延迟执行)
在遇到设置有defer属性的
defer 与相比普通 script,有两点区别:
-
- 载入 JavaScript 文件时不阻塞 HTML 的解析,执行阶段被放到 HTML 标签解析完成之后。
-
- 在加载多个 JS 脚本的时候,
async 是无顺序的加载, 而 defer 是有顺序的加载。
- 在加载多个 JS 脚本的时候,
注意点
-
对于内置而不是加载外部脚本的script标签,以及动态生成的script标签,defer属性不起作用。另外,使用defer和async属性加载的外部脚本都不应该使用document.write方法。
-
一般来说,如果异步脚本之间没有依赖关系,执行的先后顺序不影响整个页面,那么建议使用async属性;如果异步脚本之间有依赖关系就使用defer属性。
-
如果同时使用async属性和defer属性,defer属性失效,浏览器的行为由async决定。
6. Vue中的异步渲染: nextTick
官方文档:
https://vue-js.com/learn-vue/instanceMethods/lifecycle.html#_3-vm-nexttick
概念
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
原理:
vue更新dom是异步的执行,当监听到数据变化(利用objeect.defineProperty)的时候开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。
//改变数据
vm.message = 'changed'
//想要立即使用更新后的DOM。这样不行,因为设置message后DOM还没有更新
console.log(vm.$el.textContent) // 并不会得到'changed'
//这样可以,nextTick里面的代码会在DOM更新后执行
Vue.nextTick(function(){
console.log(vm.$el.textContent) //可以得到'changed'
})
宏任务(macro task) 有 setTimeout、MessageChannel、postMessage、setImmediate;
微任务(micro task)有MutationObsever 和 Promise.then。
根据浏览器兼容性的不同,nextTick选用了四种异步api,
优先级(Promise > MutationObserver > setImmediate > setTimeout)前两种是微任务,后两种是宏任务,nextTick为了更快执行,首先选用微任务,只有当浏览器不兼容才会采取宏任务方式
通过 Vue.nextTick 获取到改变后的 DOM 。通过 setTimeout(fn, 0) 也可以同样获取到。

参考: https://www.cnblogs.com/aeipyuan/p/12638597.html
7. 重排(reflow)、 重绘(repaint)
- Repaint ---- 重绘
改变某个元素的背景色、文字颜色、边框颜色等等不影响它周围或内部布局的属性时,屏幕的一部分要重画,但是元素的几何尺寸没有变。
color visibility text-decoration background-size
background background-image background-position background-repeat
outline outline-color outline-style outline-width
border-radius box-shadow border-style
- Reflow — 重排也叫回流
元件的几何尺寸变了,我们需要重新验证并计算Render Tree。是Render Tree的一部分或全部发生了变化。
引起重排:
页面首次渲染。
浏览器窗口大小发生改变。
元素尺寸或位置发生改变。
元素内容变化(文字数量或图片大小等等)。
元素字体大小变化。
添加或者删除可见的DOM元素。
激活CSS伪类(例如::hover)。
设置style属性
查询某些属性或调用某些方法。
性能提升:
1, display:none会触发reflow,而visibility:hidden只会触发repaint,因为没有发现位置变化;
2,translate属性值来替换top/left/right/bottom的切换,scale属性值替换width/height,opacity属性替换display/visibility等等;
3,动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用 requestAnimationFrame;
4,如果真的有特别耗时且不操作DOM元素的纯计算JS工作,可以考虑放到Web Workers中执行。