浏览器页面打开流程
用户输入URL
beforeunload事件触发
浏览器进程提交URL请求给网络进程
http,https,dns,缓存,cookie
content-type
如果是下载类型的contentType,会提交下载任务到下载管理器,同时导航事件结束(不会跳转?)
准备渲染进程
same-site
同协议+相同的顶级域名,
但跨域的验证更为严格:同协议+同域名+同端口
从当前页面打开一个一个samesite会复用当前页面的渲染进程(经验证无效)
提交渲染
浏览器进程接收到网络进程提交的响应头
如果是text/html
向渲染进程发送提交文档的消息,
渲染进程收到消息之后与网络进程建立管道接受数据,
文档传输完毕之后,渲染进程提交确认提交消息,
浏览器收到确认提交消息之后,更新界面状态:清空页面,更新地址栏,更新前进后退状态(history?)
![]()
如果不是text/html,如下载类型,则提交到下载管理器,结束导航
渲染
提交文档之后,渲染进程开始渲染
渲染完毕之后,通知浏览器进程,浏览器进程停止转圈
其他:
- 用户发出 URL 请求到页面开始解析的这个过程,就叫做导航
- 新页面的渲染发生在导航之后
渲染流程
输入 ---- > 处理 ----> 输出
构建DOM树
作用:
将html语法编译成浏览器能够理解的结构
DOM树的构建流程:
![]()
解析过程中使用的数据结构:
- 栈:解析 html语法
- 树:保存 DOM 树 数据结构
DOM树 和 HTML 的 区别:
结构基本相同,DOM树保存在内存中,可通过代码增删改查;通过HTML生成DOM树
样式计算
生成styleSheets
作用: 将CSS转换成浏览器能够理解的结构
来源:
style标签
link链接 ref="stylesheet"
节点的内联样式
![]()
产物:
生成styleSheets(documen.styleSheets),具备查询和修改的功能(不可增删??)
标准化
- 哪些是标准值?
DOM样式计算:生成ComputedStyle
-
继承规则
-
层叠规则
生成产物:
ComputedStyle:每个DOM节点最后生效的CSS配置以及被覆盖的CSS配置,控制台中查看
![]()
布局阶段: 构建布局树
有了DOM树 + ComputedStyle , 下一步:开始计算每个元素应该放在哪个位置
-
构建布局树(排除掉DOM中的不可见元素 ;display:none,head标签,css标签,script标签)
分层:生成图层树
构建图层树
- 拥有层叠上下文属性的元素会被提升为单独的一层
- 需要剪裁(clip)的地方也会被创建为图层。overflow:scroll
绘制:根据视口栅格化每个图层
绘制每一个图层。
通过开发者工具,layers查看页面的图层
区域 1 就是 document 的绘制列表,拖动区域 2 中的进度条可以重现列表的绘制过程
-
DOM:生成DOM树
-
Style:CSS语法解析,生成ComputedStyle
-
Layout:排列元素
-
Layer:元素分层
-
Paint:生成绘制(命令)列表
视口
用户可见区域
分块:根据选择需要合成的图块
视口很小,但是图层的面积可能很大,需要对图层进行分块来减小绘制区域
栅格化:合成位图
所以:栅格化就是将图块合成为位图的过程。栅格化一般由GPU来完成
合成 + 显示
-
将分块的图块合成为一个位图
-
通知浏览器进程,合成所有图层的位图,显示到显示器。
- tiles:图块
- raster:光栅化
- drawguad:命令:通知浏览器进程去GPU那边读取数据显示到显示器上
完整流程:
- 渲染进程将 HTML 内容转换为能够读懂的 DOM 树结构。
- 渲染引擎将 CSS 样式表转化为浏览器可以理解的 styleSheets,计算出 DOM 节点的样式。
- 创建布局树,并计算元素的布局信息。
- 对布局树进行分层,并生成分层树(图层)。
- 为每个图层生成绘制列表,并将其提交到合成线程。
- 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。
- 合成线程发送绘制图块命令 DrawQuad 给浏览器进程。浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。
其他
重排(重新布局):主线程
重排的花销跟render tree有多少节点需要新构建有关系,假如在body最前面插入一个元素,会导致整个render tree回流,但如果是指body后面插入一个元素,则不会影响前面的元素重排。
当页面布局和几何属性改变时就需要重排。
重绘:主线程
直接合成:非主线程
规定:transform 不会改变元素的布局
JS,CSS对DOM解析的影响
CSS
- CSS 不会阻塞DOM的解析,但是会阻塞DOM的渲染
- CSS 会阻塞定义在其后的JS的执行
- 虽然CSS不会阻塞DOM的解析,但是JS会,所以如果CSS后面如果还有JS(非async,defer),这种情况下CSS简介的阻塞了DOM的解析
JS
- JS 会阻塞DOM的解析
- 动态插入的脚本不会阻塞页面解析,并在加载完成之后立即执行;可以设置script.async = false,设置为按照defer模式的顺序执行
- defer 和 async 都只能用于外部脚本,如果 script 没有 src 属性,则会忽略它们。
- defer ,async: 并行下载;
- 多个
defer的脚本执行顺序严格按照定义顺序进行,而不是先下载好的先执行;- 多个
async,按照谁先下载完成谁先执行的原则进行,所以当它们之间有顺序依赖的时候特别容易出错。defer脚本下载完成后,执行时间一定是DOMContentLoaded事件触发之前执行;async脚本的执行 和DOMContentLoaded的触发顺序无法明确谁先谁后,因为脚本可能在DOM构建完成时还没下载完,也可能早就下载好了;
事件循环
JS执行机制(删)
编译阶段
创建执行上下文
有三种情况下执行JS代码会创建上下文
- 执行、加载一个JS模块(文件):
- 会编译全局代码(最外层的代码,不在其他上下文中的代码)
- 并创建全局执行上下文(唯一)
- 页面销毁时清除上下文
- 调用一个函数:
- 编译函数体内的代码,
- 函数调用时才会被编译
- 创建函数执行上下文
- 函数退出后清除上下文
- 编译函数体内的代码,
- eval:
- 调用eval时编译代码,
- 创建eval上下文
class:- class的上下文为class实例对象
执行阶段
调用栈(执行上下文栈)
比Java中的调用栈复杂,Java没有上下文栈这中概念,Java中万物皆对象(class),this,就是当前对象
Java的调用栈只保存方法的局部变量
console.trace() 输出调用栈
变量环境
通过 var ,function 声明;声明的对象会被保存在最近的上下文环境中
词法环境
通过let,const 声明;声明的对象会被保存在最近的词法环境中
能生成上下文环境的都可以生成词法环境,方法块也可以生成词法环境
var的创建和初始化被提升,赋值不会被提升。 let的创建被提升,初始化和赋值不会被提升。 function的创建、初始化和赋值均会被提升。
Temporal Dead Zone,指的是从block创建到初始化完成之间的时间
在初始化之前使用对象会引发:
Uncaught ReferenceError: Cannot access 'myname' before initialization
作用域链:JavaScript 语言的作用域链是由词法作用域决定的,而词法作用域是由代码结构来确定的。
作用域链和 this 是两套不同的系统,它们之间基本没太多联系
使用对象来调用其内部的一个方法,该方法的 this 是指向对象本身的