高级JS-浏览器渲染-运行原理

91 阅读5分钟
  • 网页被解析的过程
image.png
  • 浏览器的内核

image.png

一个网页下载下来后,就是由渲染引擎来帮助我们解析的

渲染页面的详细流程:www.html5rocks.com/en/tutorial…

  • 构建Render Tree(渲染树)
  1. link元素不会阻塞DOM树的构建过程,但是会阻塞渲染树的构建过程
  2. 渲染树和DOM树并不是一一对应的关系,比如display为none的元素,压根不会出现在渲染树中

image.png

总结:

  • 首先通过DNS服务器进行域名解析
  • 解析出对应的IP地址 然后从ip地址对应的主机发送http请求 获取对应的静态资源
  • 默认情况服务器会返回index.html文件
  • 然后浏览器内核开始解析HTML
  • 首先 会解析对应的html 生成DOM Tree
  • 解析过程中 如果遇到css的link标签 则会下载对应的css文件
  • 下载css文件和生成DOM树是同时进行
  • 下载完对应的css文件后会进行css解析 生成CSSOM( CSS object model)
  • 当DOM Tree和CSSTree都解析完成之后 会进行合并用来生成Render Tree(渲染树)
  • 初步生成的渲染树会显示节点以及部分样式 但是并不表示每个节点的尺寸 位置信息 于是进行Layout(布局)来生成渲染树中节点的宽度 高度位置信息
  • 经过Layout之后 浏览器内核将布局时的每个frame转屏幕上的每个像素点 将每个节点绘制到屏幕上

回流和重绘

第一次确定节点大小和位置,称之为布局之后对节点的大小、位置修改重新计算称之为回流

尽量避免发生回流:

  1. 修改样式时尽量一次性修改,比如通过cssText修改,比如通过添加class修改
  2. 尽量避免频繁的操作DOM
  3. 尽量避免通过getComputedStyle获取尺寸、位置等信息
  4. 对某些元素使用position的absolute或者fixed并不是不会引起回流,而是开销相对较小,不会对 其他元素造成影响

第一次渲染内容称之为绘制(paint),之后重新渲染称之为重绘,比如修改背景色、文字颜色、边框颜色、样式等会引起重绘

  • defer属性

defer 属性告诉浏览器不要等待脚本下载,而继续解析HTML,构建DOM Tree

  1. 脚本会由浏览器来进行下载,但是不会阻塞DOM Tree的构建过程;
  2. 脚本提前下好了,会等待DOM树构建完成,在DOMContentLoaded事件之前先执行defer中的代码
  3. defer可以提高页面的性能,并且推荐放到head元素中
  4. 注意:defer仅适用于外部脚本,对于script默认内容会被忽略
  • async属性

async它也能够让脚本不阻塞页面,是让一个脚本完全独立的

  1. 浏览器不会因 async 脚本而阻塞(与 defer 类似
  2. async脚本不能保证顺序,它是独立下载、独立运行,不会等待其他脚本
  3. async不能保证在DOMContentLoaded之前或者之后执行

defer通常用于需要在文档解析后操作DOM的JavaScript代码,并且对多个script文件有顺序要求的;

async通常用于独立的脚本,对其他脚本,甚至DOM没有依赖的;

JS运行原理

  • V8引擎

image.png

Parse模块会将JavaScript代码转换成AST(抽象语法树),如果函数没有被调用,是不会被转换成AST

Ignition是一个解释器,会将AST转换成ByteCode(字节码)

TurboFan是一个编译器,可以将字节码编译为CPU可以直接执行的机器码

  • 初始化全局对象

js引擎会在执行代码之前,会在堆内存中创建一个全局对象:Global Object(GO)

  1. 该对象 所有的作用域(scope)都可以访问
  2. 里面会包含Date、Array、String、Number、setTimeout、setInterval等等
  3. 其中还有一个window属性指向自己
  • 执行上下文

js引擎内部有一个执行上下文栈(Execution Context Stack,简称ECS),用于执行代码的调用栈

每一个执行上下文会关联一个VO(Variable Object,变量对象),变量和函数声明会被添加到这个VO对象中

当全局代码被执行的时候,VO就是GO对象了

在执行的过程中执行到一个函数时,就会根据函数体创建一个函数执行上下文(Functional Execution Context,简称FEC),并且压入到执行上下文中

  1. 当进入一个函数执行上下文时,会创建一个AO对象(Activation Object);
  2. 这个AO对象会使用arguments作为初始化,并且初始值是传入的参数;
  3. 这个AO对象会作为执行上下文的VO来存放变量的初始化;

总结

  • 首先在执行前会在堆内存中开辟一块空间(GO) 存放一些初始的值 如Number String等等
  • 代码中定义的一些变量 函数 ( 在解析器转成抽象语法树的过程中存放在GO中的 ) 并没有赋值
  • 同时在执行代码时在执行上下文栈(ECS)中存放一个全局执行上下文(GEC) 用于执行代码
    • GO中对应的函数也会在堆内存中开辟出空间为 Function Object 初始一些数据(name length scope chain等)
  • 开始执行代码
  • 每个EC中有着三个重要的内容(VO scope chain 以及this)
  • VO指向对应的作用域(全局作用域(GO) 函数作用域(AO))

作用域和作用域链

作用域链是一个对象列表,用于变量标识符的求值;当进入一个执行上下文时,这个作用域链被创建,并且根据代码类型,添加一系列的对象