参考优质博文:从输入URL到页面加载的过程?如何由一道题完善自己的前端知识体系!
浏览器分配一个进程(浏览器渲染线程)给这个页面
页面是多线程的:
- http请求会被分配一个异步http请求线程,
- 页面渲染分配一个GUI渲染线程,
- JS代码涉及到JS引擎线程、事件触发线程、计时器触发器线程
- 代码中的setTimeout和setInterval等计时器,会放入计时器触发器线程,到时间了回调函数就被推入执行事件队列
- 事件触发线程是浏览器给JS引擎线程开的外挂,用来控制事件循环的。
- JS引擎线程用于执行JS代码,从事件队列中取事件执行,JS引擎线程和GUI渲染线程是互斥的,所以JS代码如果太拉了,就会影响页面渲染。
强缓存判断,如果缓存失效了的话,进行下面的
URL解析:
- 如果host不是ip地址,就拿着host去DNS域名服务器进行查询,中间可能经过路由、缓存
- 拿到了ip地址,准备握手建立连接。
三次握手建立TCP连接
- 客户端:外,哥们,开个门,听到了没,听到了吱一声。(试图确认服务端的正常收发能力)
- 服务端:听到了,超,我听到了,你听到我了吗。(确认了客户端的正常发送能力,试图确认客户端的正常接收能力)
- 客户端:听到了,ok,可以建立连接了。(客户端确认了服务端的正常收发能力)
- 服务端收到最后一次招呼,确认了客户端的正常接收能力,连接建立。
https的话再进行 SSL/TLS 握手
- 客户端:生成一个随机数,将随机数A和一系列加密算法发送给服务端
- 服务端:收到,存下A,挑选一组加密算法Z和hash算法,生成一个随机数B,将随机数B和加密算法Z发送给客户端。然后,自己的网址+自己的公钥+证书颁发机构的信息打包 = 证书,发送给客户端。一共两个包,两次发送
- 客户端:收到,验证证书是否值得信任,完事儿拿到服务端的公钥,存下随机数B,自己生成一个随机数C,用服务端公钥和加密算法Z加密C。用A+B+C按照一定方式生成对称密钥X,使用X加密一段明文信息得到密文,然后用约定方式hash明文信息,得到hash段,加密了的C+密文+hash段打包发送给服务端
- 服务端:收到,用私钥和加密算法Z解密得到C,用相同的一定方式将A+B+C生成对称密钥X,用X解密密文得到明文信息,用约定的hash算法hash同一段明文信息得到服务端hash段,同客户端发来的hash段进行比较,一致则说明ok。然后使用X加密一段明文得到密文,并hash这段明文,hash段和密文一同发送给客户端
- 客户端:收到,用X解密密文得到明文,明文进行hash,和服务端发来的hash端进行比较,一致则说明可以建立加密通道。使用X进行对称加密通话。
发出正式的 http 请求
- tcp连接建立了就可以正式发送请求了。
- get请求就只发送一个包,post的话先发送请求头header,得到status=100后,再发送body请求体(火狐浏览器的话post也是只发一个包),返回status=200的话,ok。
协商缓存判断,如果缓存失效了的话,进行下面的
- 如果上面返回的只是一个返回头,然后返回头的
E-tag 和浏览器请求头的If-None-Match 匹配,说明304,使用本地的协商缓存,http1.1。如果是http1.0,则是服务端Last-Modified 和客户端If-Modified-Since 两个头字段进行匹配。
后台交互,把资源发给前端
前端拿到返回资源,解析
- 解析HTML,构建DOM树,
bytes-characters-tokens-nodes-DOM
- 转换:bytes->字符
- 分词:字符->词元(有了自己的含义)
- 词法分析:词元->对象(有了自己的属性和规则)
- DOM构建:对象->树形结构(对象之间有了关系)
- 解析CSS,构建CSS规则树,
bytes-characters-tokens-nodes-CSSOM - 合成DOM树和CSS规则树,生成render树
- 将两棵树合成,样式和DOM元素合成,一些不可见的元素(存在于DOM树中的)就不会进入渲染树
- 布局render树,引起回流(reflow|重排),元素的尺寸、位置计算(这中间可能就有一些JS代码影响了渲染树,导致了回流和重绘)(用本地缓存存储一些访问时会导致回流的变量,可以降低回流的频率)
- 绘制render树(paint),绘制页面像素信息
- 浏览器将各层信息发送给GPU,GPU将各层合成(composite),显示在屏幕上
- 单独的,外链资源的加载:
- img资源,异步下载,不会阻塞各种解析,下载完就直接替换原有src的位置
- css资源,不会阻塞HTML的解析,但是会阻塞渲染树的构建(media query声明的css资源不会阻塞)
- js资源,会阻塞HTML的解析,下载完并执行后才会继续HTML的解析。
- 如果加了defer,下载完之后等待到html解析完了才会执行,是延迟执行;如果加了async,下载完就立即执行,没有先后顺序,谁先下载完谁先执行,所以可能造成js之间的阻塞,是异步下载。
- 有的浏览器也有优化,可以并发下载,但也只是优化下载过程,解析和执行过程的阻塞还是管不着的。
- JS引擎解析(单独拿出来讲):
- 解释阶段
- 词法分析,代码->词元
- 语法分析,词元->语法树
- 使用翻译器将代码转换成字节码(bytecode),谷歌的V8引擎会直接把代码转换成机器码。
- 使用字节码解释器将字节码转换成机器码
- 预处理阶段,为了确保JS代码正确执行
- 执行阶段
- 执行上下文,下面三个都是执行上下文的三个属性;执行堆栈。
- 执行上下文都存在一个栈中
- 浏览器组开始执行JS,创建一个全局执行上下文,压入执行栈
- 然后每进入一个作用域,就创建对应的执行上下文压入栈顶
- 每从一个作用域退出,就把栈顶的执行上下文弹出来,将上下文控制权交给栈顶元素
- 最后都会回到全局执行上下文。
- VO和AO,对象变量和活动变量:函数中
AO === VO ,全局中AO === this === global
- 对象变量和当前执行上下文有关。
- 活动变量是函数被调用者激活之后出现的。
- 作用域链,类似于原型链,变量的寻找是从内向外的,一层一层,最外面都找不到那就报错
- this,this引出的变量,本层找不到的话不会沿着作用域链找,之和当前上下文有关。
- 回收机制(我看看书再补充)
用户看到页面
|