一文搞懂浏览器内核
持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天
浏览器内核组成
先上图
- WebCore : 主要负责HTML解析、布局、渲染等相关的工作,可以理解为主要为渲染页面而服务,也被称为渲染引擎。
- JsCore(JavaScriptCore):解析+执行js代码,也被称为JavaScript引擎。
WebCore (渲染引擎)
渲染流程图
DOM树/CSSOM树的形成
需要了解几点
- 浏览器第一步会去解析HTML文件
- 网络中传输的其实都是0和1这种字节数据,因此浏览器需要把字节数据转化为字符串
- 转化为字符串之后根据词法分析,将字符串转为标记(token)代码中的最小单位
- 最后将标记转化为树的节点Node , 树的节点拼接成DOM树。
- 同样的原理,css文件形成CSSOM树。
渲染树的形成
参考一张图
- 渲染树只会显示需要显示的节点,eg:display:none 在渲染树上就不会显示
- 形成渲染树之后,浏览器会根据渲染树进行页面的一个布局(也叫回流)
- 然后调用GPU绘制,最终显示。
渲染过程中遇到了JavaScript代码怎么办?
- JavaScript会阻塞渲染引擎的,待js代码执行完成后,继续进行渲染。(同步)
-
为什么要同步进行?
- JavaScript可以操作DOM元素
- 浏览器希望JavaScript操作完成之后再进行生成dom树,不去频繁的生成dom树
-
如何避免js代码的阻塞
- Script标签属性 defer 、 script
重绘和回流
回流和重绘在我们设置节点样式的时候频繁的出现,同时也会很大方面影响性能
- 重绘是指当前节点只需要修改外观而不需要进行布局上的变化
- 回流指的是节点的布局和几何属性需要发生改变
- 回流一定重绘, 回流成本更高
-
如何减少回流和重绘?
- 用transform 代替 top
- 使用 visibility 代替 display:none
- 动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用
requestAnimationFrame
为什么操作dom会慢?
- 跨线程之间的通信,生成dom是通过渲染引擎,执行js代码是使用js引擎,跨线程之间的通信,势必会造成性能上的损耗
假如要插入1万个dom,如何使页面不卡顿?
- 使用
requestAnimationFrame的方式插入
- 虚拟滚动 , 滚到滚轮的最下端 再进行插入 ,多次少量加载
JavaScriptCore(V8 引擎)
我们以Chrome内核的V8引擎为例
V8引擎的定义
- 使用C++ 编写的Google开源高性能JavaScript 和 WebAssembly引擎,它用于Chrome和Node.js等
- 他实现ECMAScript和WebAssembly,并在Windows7或更高的版本,macOS10.12+和使用x64,IA-32,ARM或MIPS处理器的Linux系统上运行
- V8 可以独立的运行,也可以嵌入到任何C++应用程序中
V8引擎的原理
V8官方文档:v8.dev/blog
v8引擎执行流程
- 主要通过 转化 - 解释 - 运行 三个步骤
重要模块解析
-
Parse模块
-
Parse会将JavaScript源码转化成抽象语法树,因为解释器并不直接可以识别JavaScript代码
- 如果函数没有调用,是不会被转化成AST的
-
-
Ignition模块
-
Ignition是一个解释器,会将AST转化为ByteCode(字节码)
- 在执行的同时会收集TurboFan优化所需要的信息(比如说函数参数的类型信息)
- 如果函数是第一次的运行,会直接转化为ByteCode
-
-
TurboFan模块
-
TurboFan 是一个编译器,可以将字节码编译成CPU可以直接执行的机器码
-
如果一个函数被多次的调用,那么就会被标记为热点函数,那么就会被TurboFan转化成优化过的机器码,提高代码的执行性能
-
但是机器码实际上也会反向优化,将机器码转化为字节码。因为如果后续执行函数中,类型发生了改变,之前优化的代码并不嫩正确的处理,就会逆向的转化为字节码
- 举个例子:add(a:numbr , b:number)函数 突然传入两个字符串的情况就需要进行反向的优化。
- 使用TypeScript 可以很好的避免这个问题
-
-
-
Orinoco模块
-
Orinoco模块主要负责:垃圾回收 ,将不用的变量的空间回收
-
垃圾回收的方式:
-
标记清除(主流)
- 进入上下文离开上下文都会被标记
- 标记内存中的所有变量,去掉所有上下文中的变量以及引用的变量,剩下的带有标记的就是待删除的
- 缺点:内存碎片块
-
引用计数
- 记录每个变量被引用的次数
- 引用+1
- 被覆盖 -1
- 直到为0 说明此值访问不到了 就清理回收空间
- 缺点:可能导致循环引用的问题 内存泄漏
-
-
为了追求性能,如何减少垃圾回收的次数?
-
对于不用的变量解除引用,变量设置为null
-
隐藏类
-
对象
- 对象池,减少新对象的生成
-
-
什么会内存泄漏?
- 未定义就使用的全局变量
- 不合理的闭包
- 遗忘的定时器
-
参考文献
- codeerwhy老师 《深入了解node.js》
- 《javaScript高级程序设计》
- 修言《前端面试之道》