一文搞懂 浏览器内核

281 阅读5分钟

一文搞懂浏览器内核

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天

浏览器内核组成

先上图

流程图.jpg

  • WebCore : 主要负责HTML解析、布局、渲染等相关的工作,可以理解为主要为渲染页面而服务,也被称为渲染引擎。
  • JsCore(JavaScriptCore):解析+执行js代码,也被称为JavaScript引擎。

WebCore (渲染引擎)

渲染流程图

流程图 (1).jpg

DOM树/CSSOM树的形成

需要了解几点

  • 浏览器第一步会去解析HTML文件
  • 网络中传输的其实都是0和1这种字节数据,因此浏览器需要把字节数据转化为字符串
  • 转化为字符串之后根据词法分析,将字符串转为标记(token)代码中的最小单位
  • 最后将标记转化为树的节点Node , 树的节点拼接成DOM树。
  • 同样的原理,css文件形成CSSOM树。

流程图 (2).jpg

流程图 (3).jpg

渲染树的形成

参考一张图

  • 渲染树只会显示需要显示的节点,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引擎执行流程

UML 图.jpg

  • 主要通过 转化 - 解释 - 运行 三个步骤

重要模块解析

  • 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高级程序设计》
  • 修言《前端面试之道》