一.、从输入url到展示的过程
1.输入url
2.发起请求(URL解析/DNS解析)
3.网络连接(三次握手)
4.服务器响应请求,返回数据
5.浏览器加载/渲染页面
二、浏览器构成
1.User Interface(用户界面)
包括地址栏,前进/后退按钮,书签菜单等。除了显示你请求的页面外,其他显示的各个部分都属于用户页面
2.Browser engine(浏览器引擎)
在用户界面和渲染引擎之间传送数据
3.Rending engine(渲染引擎)
负责显示请求的内容,对于html,负责解析html和css内容,并将解析后的内容显示在屏幕上
4.Networking(网络)
用于网络调用,比如http请求,接口与平台无关,并为所有平台提供底层实现
5.JavaScript Interpreter(javascript解释器)
用于解析和执行javascript代码
6.UI Backend(UI后端)
用于绘制基本的窗口小部件,比如组合框和窗口,其公开了与平台无关的通用接口,而在底层使用操作的用户界面方法
7.Data Persistence(数据持久性)
这是持久层。浏览器需要在硬盘上保存各种数据,例如Cookie。新的HTML规范(HTML5)定义了“网络数据库”,这是一个完整(但是轻便)的浏览器内数据库。
三、浏览器内核
浏览器的主要功能就是向服务器发出请求,在浏览器窗口中展示您选择的网络资源。
浏览器的内核是指支持浏览器运行的最核心的程序,分为两个部分:
1.渲染引擎
2.JS引擎
四、浏览器加载/渲染页面——渲染引擎
1.解析HTMI为DOM树
html字节流变成字符流
词法分析:将字符流解析为一个个词语
语法分析:通过不同的标签生成node节点
构建DOM树:将node节点组织成DOM树
解析外部CSS文件及style标签中的样式信息,得到元素最匹配的样式:
1)经过词法分析和语法分析,生成一个CSS规则
2)进行规则匹配
2.渲染树结构
生成RenderObject树:由DOM树构建RenderObject树,并将CSS得到的元素匹配样式存入到RenderObject树中
3.布局渲染树
根据RenderObject中的样式属性,计算根据框模型,计算布局
4.绘制渲染树
先绘制元素背景,然后是浮动,最后是前景。最终得到用户可见区域(ViewPort)的内存表示
五、页面加载过程中HTML,CSS,JavaScript加载顺序
通常页面的加载速度会受HTML,CSS ,JS 的影响
浏览器接收到HTML模板文件,开始从上到下解析HTML
遇到样式表文件style.css,这时候浏览器停止解析HTML,接着去请求CSS文件
服务端返回CSS文件,浏览器开始解析CSS文件
浏览器解析完CSS,继续往下解析HTML,碰到DOM节点,解析DOM
浏览器发现img,向服务器发送请求。此时浏览器不会等到图片下载完,而是继续渲染后面的代码
-
服务器返回图片文件,由于图片占据了一定的面积,影响了页面排布,因此浏览器需要回过头来重新渲染这部分代码
reflow(回流):当浏览器发现页面中某个部分发生了点变化影响了布局,需要倒回去重新渲染,这个过程为reflow
repaint(重绘):改变某个元素的背景色,字体颜色,边框颜色等等不会影响布局的时候,屏幕的一部分呢需要重画,但是元素的布局不会发生变化
碰到脚本文件,这时停止所有的加载与解析,去请求脚本文件,并执行脚本
加载完所有的HTML,CSS,JS后,页面就出现在屏幕上了
渲染引擎会尽可能早的将内容呈现到屏幕上,并不会等到所有的html解析完后才去构建和布局render树。它是解析完一部分就下载一部分,同时还可能在下载别的内容
六、关于CSS阻塞和JS阻塞
1.CSS会阻塞渲染吗?
从浏览器的渲染原理来看,渲染引擎会将css构建成CSSOM Tree然后再去渲染页面,也就是说css会阻塞页面的渲染。
但是css不会阻塞页面的解析,因为需要具有DOM以及CSSOM才会构建渲染树-
2.JS会阻塞渲染吗?
加载或执行js代码时,会阻塞构建DOM树,只有等到js执行完毕,浏览器才会继续解析DOM。
没有DOM树,浏览器就无法渲染,所以当加载很大的JS文件时,可以看到页面很长时间时一片空白。
这是因为在加载js的过程中会操作DOM节点,这些操作会对DOM树产生影响,如果不阻塞,等浏览器解析完标签生成DOM树后,JS修改了某些节点,那么浏览器又得重新解析,然后重新生成DOM树,耗费性能
3.解决JS阻塞DOM树生成
defer 和 async 都是作用于外链JS的。对内部JS是没有效果的。
defer 和 async 都是异步的,主要的区别在于执行的顺序以及执行的时间
async 标志的脚本文件一旦加载完成后就会立即执行,并且不会按照书写顺序,谁下载好了就直接执行。所以适应于那些没有代码依赖顺序,并且没有DOM操作的脚本文件
defer 标志的脚本文件会严格按照书写顺序去执行,并且会在页面DOM加载完成时执行,适用于有DOM操作,或者是有代码依赖顺序的脚本文件。
七、JS代码由谁来执行呢?——JS引擎(比如V8引擎)
1.为什么需要JS引擎?
高级语言都是转成机器指令来执行的
我们编写的JavaScript代码无论交给浏览器还是Node,最终都会交给CPU来执行
但是CPU只认识自己的指令集,实际上是机器语言,才能被CPU执行
所以我们需要JavaScript引擎帮助我们将JavaScript代码翻译成CPU指令来执行
2.V8引擎
1)V8基本概念
V8是用C++编写的Google开源高性能JavaScript和WebAssembly引擎,它作用于Chrome和Node.js等,可以作用在很多系统上,可以独立运行,也可以嵌入到任何C++程序中
2)V8的架构
Parse模块会将JavaScript代码转为AST树(抽象语法树),这是因为解释器并不直接认识JavaScript代码。如果函数没有被调用,是不会转成AST的
-
Ignition是一个解释器,会将AST转为ByteCode(字节码)
同时会收集TurboFan优化所需要的信息(比如函数参数的类型信息,有了类型才能进行真实的运算)
如果函数只执行一次,Ignition会执行解释ByteCode
-
TurboFan是一个编译器,可以将字节码翻译为CPU直接执行的机器码
如果一个函数被多次调用,那么就会标记为热点函数,那么就会经过TurboFan转化成优化的机器码,以后CPU直接运行机器码,提高代码的执行性能。
但是,机器码也会还原为ByteCode,这是因为如果后续执行函数的过程中,类型发生了变化(比如sum函数原来是number类型,后来变为string类型),之前优化的机器码并不能正确的处理运算,就会逆向的转为字节码
3)V8引擎的解析图(官方)
[图片上传失败...(image-113b81-1645708377771)]
Blink将源码交给V8引擎,Stream获取到源码并且进行编码转换
Scanner会进行词法分析,词法分析会将代码转换成tokens
-
接下来tokens会经过Preparser和Parser转换为AST树
Parser就是直接将token转换为AST树架构
Preparser称之为预解析,为什么需要与解析呢?
<u style="box-sizing: border-box;">这是因为并不是所有的JavaScript代码,在一开始就会被执行,那么对的所有的JavaScript代码进行解析,必然会影响网页的效率</u>
<u style="box-sizing: border-box;">所以V8引擎就实现了Lazy Parsering(延迟解析)的方案,它的作用是将不必要的函数进行与解析,也就是只解析暂时需要的内容,而对函数的全量解析是在函数被调用时才会进行</u>
<u style="box-sizing: border-box;">比如我们在一个函数oute内部定义了另外一个函数inner,那么inner函数就会进行预解析</u>
生成AST树后,会被Ignition转为字节码,之后的过程就是代码的执行过程(下一篇进行详细讲解)
备注:本文部分内容都来自coderwhy老师,感兴趣的小伙伴可以去简书或者github搜索coderwhy,会有很多收获-
参考链接:
1.https://www.jianshu.com/p/7401a240f21f
2.https://www.jianshu.com/p/4a942a7dc153
3.https://segmentfault.com/a/1190000017476386
4.coderwhy