浏览器渲染原理

本文涉及的产品
.cn 域名,1个 12个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: 浏览器渲染原理

浏览器渲染原理

1. 进程和线程

「进程 :」 进程是操作系统资源分配的基本单位,进程中包含线程。简而言之,就是正在进行中的应用程序。

「线程」:线程是由进程所管理的。是进程内的一个独立执行的单位,是CPU调度的最小单位。

  • 「线程」「进程」的基本单位,一个进程由一个或者多个线程组成,搞清楚这个关系之后,我们可以明确「线程就是程序执行的最小单元」
  • 线程和进程一样,也是动态概念,有创建有销毁,存在只是暂时的,不是永久性的。
  • 进程与线程的区别在于「进程在运行时拥有独立的内存空间」,也就是说「每个进程所占用的内存都是独立的」
  • 例如:微信运行时,系统会给它一个运行内存。
  • 「多个线程是共享内存空间的」,但是每个线程的执行是相互独立的,线程必须依赖于进程才能执行,单独的线程是无法执行的,由进程来控制多个线程的执行,没有进程就不存在线程。
  • 例如:我先开启一个发送消息的线程,那么同时还能由一个接收消息的线程。两个线程之间完全独立。

「为了提升浏览器的稳定性和安全性,浏览器采取了多进程模型。」

2. 浏览器中的5个进程

目前最新的Chrome进程架构图

449142119fc99d7771c90c43828c39f1.png08d7d7c0f84be67179a9599cf3ae5846.png

浏览器设置的时候是一个多进程模型,这样能确保浏览的安全性和稳定性。如果一个页面有问题,不影响其他页面的运行。

  • 「浏览器进程。「主要负责」界面显示」「用户交互」「子进程管理」、同时提供「存储」等功能。
  • 「渲染进程。」 核心任务是将HTMLCSSJavaScript转换为「用户可以与之交互的网页」,排版引擎BlinkJavaScript引擎V8都运行在该进程中,默认情况下,Chrome为每一个Tab标签页创建一个渲染进程。出于安全考虑,渲染进程都是运行在「沙箱模式」下的。
  • **GPU进程。**GPU图形处理器(英语:graphics processing unit,缩写:GPU),负责3D CSS效果,网页,Chrome ui的绘制。
  • 「网络进程」。主要负责页面的「网络资源加载」,之前是作为一个模块运行在浏览器进程里面的,直至最近才独立处理,成为单独一个进程。
  • 「插件进程」。主要负责「插件的运行」,因为插件易崩溃,所以通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响。每一种类型的插件对应一个进程,仅当使用该插件时才创建。

所以我们开启一个页面,至少会启动4个进程。

3. HTTP 请求流程

HTTP是一种允许浏览器向服务器获取资源的协议,是Web的基础。通常由浏览器发起请求,用来获取不同类型的文件,例如HTMLCSSJavaScript「图片」「视频」等。此外,HTTP也是浏览器使用最广的协议。规定了客户端请求,和服务器端响应数据格式的协议。

接下来简单介绍一下 浏览器发送HTTP 请求的大致流程:

3.1 浏览器发送 HTTP 请求的流程

  1. 「构造请求」

首先,浏览器构造请求行,构建好之后,浏览器准备发起网络请求

  1. 「查找缓存」

在正在发起网络请求之前,浏览器会现在浏览器缓存中查询是否有请求的文件,其实「浏览器缓存是一种本地保存的资源副本,以供下次请求时直接使用的技术。」

当浏览器发现请求的资源已经在浏览器缓存中存有副本,它会拦截请求,返回该资源的副本,并直接结束请求。而不会再去源服务器中重新下载。这样可以「缓解服务的压力,提升性能」。如果缓存查找失败,则进入网络请求。

  1. 「准备IP地址和端口」

HTTP和TCP的关系,因为浏览器使用HTTP协议作为应用层协议**,用来封装请求的文本信息**;并使用TCP/IP作传输层协议将它发到网络上,所以在HTTP工作开始之前,浏览器需要TCP与服务器建立连接。也就是说HTTP的内容是通过TCP的传输数据阶段来实现的。

ef33ded35e5c03e1b1bfb36f8cfa0619.png

数据包是通过IP地址传输给接收方的。由于IP地址是数字标识的,难以记忆,使用一个域名例如www.baidu.com就容易记忆了,所以基于这个需求又出现了一个服务,负责把域名和IP地址做--映射关系。这套域名映射为IP的系统叫做"域名系统",简称DNS

第一步浏览器会请求 DNS 返回域名对应的 IP。「当然浏览器还提供了 DNS 数据缓存服务」,如果某个域名已经解析过了,那么浏览器会缓存解析的结果,以供下次查询时直接使用,这样也会减少一次网络请求。

  1. 「等待TCP队列」

IP地址和端口已经准备好了,是不是可以马上建立TCP连接。

不行,因为Chrome有个机制,同一个域名同时最多只能建立6个TCP连接。如果请求书少于6个,直接进入下一步,建立TCP连接。

  1. 「建立TCP连接」

排队等待结束后,建立TCP连接

  1. 「发送HTTP请求」

3.2 服务端处理 HTTP 请求的流程

历经千辛万苦,HTTP 的请求信息终于被送达了服务器。接下来,服务器会根据浏览器的请求信息来准备相应的内容:

  1. 「返回请求」
  2. 「断开连接」

通常情况下,一旦服务器向客户端返回了请求数据,它就要关闭TCP连接。不过如果在浏览器或服务器在其头部信息加入Connection:Keep-Alive那么TCP连接在发送后将仍然保持打开状态,这样浏览器可以继续通过同一个TCP连接发送请求。

保持TCP连接可以省去下次请求时需要建立连接的时间,提升资源加载速度。比如一个Web页面中内嵌图片来自于同一个web站点,如果初始化长连接,就不需要重复建立新的TCP连接。

3.3 为什么很多站点第二次打开会很快

因为第一次加载页面的过程中,缓存了一些耗时的数据。

那么,哪些数据会被缓存呢?「DNS缓存」「页面资源缓存」这两块数据是会被浏览器缓存的。

1cdd6aaf5439afbbbcc857cf9fb570e4.png

通过上图第一次请求可以看出,当服务器返回HTTP响应头给浏览器时,浏览器通过响应头的Cache-Control字段来设置是否缓存该资源。

Cache-Control:Max-age=2000 //缓存过期时间是2000

这也就意味着,在该缓存资源还没有过期的情况下,如果再次发送请求该资源,会之间返回缓存中的资源给浏览器。

但如果缓存过期了,浏览器则会继续发送网络请求,并且在HTTP请求头中带上:

If-None-Match:"4f80f-13c-3a1xb12a"

简要来说,很多网站第二次访问能够秒开,是因为这些网站把很多资源都缓存在了本地,浏览器缓存直接使用本地副本来回应请求,而不会产生真实的网络请求,从而节省了时间。同时,DNS 数据也被浏览器缓存了,这又省去了 DNS 查询环节。

4. 输入url地址到浏览器显示页面发生了什么

接下来我们从进程角度讨论一下:从浏览器里,输入URL地址,到页面显示,这中间发生了什么?

84e6fa1912765ced41b1d434a4dadc98.png

从上图可以看到,整个过程需要各个进程之间的配合,我们结合上图我们从进程的角度,描述一下

1、「浏览器进程」接收到用户输入的URL请求,「浏览器进程」便将URL转发给网络进程。

2、「网络进程」中发起真正的URL请求。

3、「网络进程」接收到响应头数据,便解析响应头数据,并将数据转发给「浏览器进程」

4、「浏览器进程」接收到网络进程的响应头数据之后,发送"「提交文档」"消息到「渲染进程」

5、「渲染进程」接收到"提交文档"的消息之后,便开始准备接收HTML数据,接收数据的方式是直接和「网络进程」建立「数据管道。」

6、等文档数据传输完成之后,「渲染进程」会返回“确认提交”的消息给「浏览器进程」

7、「浏览器进程」接收到「渲染进程」"确认提交"的消息之后,便开始移除之前旧的文档,然后更新「浏览器进程」中的页面状态。

所谓提交文档,就是「浏览器主进程」「将网络进程接收到的HTML数据提交给渲染进程」

5. 浏览器渲染流程

接下来我们从一个简单的html页面来谈浏览器的渲染流程:

5.1 构建 DOM 树

DOM解析的特点,是不会被阻塞的。因为浏览器无法直接理解和使用HTML,所以需要将HTML转化为浏览器能够理解的结构—DOM树。树结构很像我们现实生活中的"树",其中的每一个点我们称为**节点,**相连的节点称为父子节点。在浏览器渲染中,我们使用的就是树结构。

DOM树描述了文档的内容。<html>元素是第一个标签也是文档树的根节点。树反映了不同标记之间的关系和层次结构。嵌套在其他标记中的标记是子节点。DOM节点的数量越多,构建DOM树所需的时间就越长。

HTML内容转换为浏览器DOM树结构的过程:字节 → 字符 → 令牌 → 节点 → 对象模型。

2855b50e548657a5d8ff7e56ae681b3e.png

  • 转换:  浏览器从磁盘或网络读取 HTML 的原始字节,并根据文件的指定编码(例如 UTF-8)将它们转换成各个字符。
  • 令牌化: 浏览器将字符串转换成 W3C HTML5 标准规定的各种令牌,例如,“”、“”,以及其他尖括号内的字符串。每个令牌都具有特殊含义和一组规则。
  • 词法分析:  发出的令牌转换成定义其属性和规则的“对象”。
  • DOM 构建:  最后,由于 HTML 标记定义不同标记之间的关系(一些标记包含在其他标记内),创建的对象链接在一个树数据结构内,此结构也会捕获原始标记中定义的父项-子项关系: HTML 对象是 body 对象的父项,body 是 paragraph 对象的父项,依此类推。

当解析器发现非阻塞资源,例如一张图片,浏览器会请求这些资源并且继续解析。当遇到一个CSS文件时,解析也可以继续进行,但是对于<script>标签(特别是没有 async 或者 defer 属性)会阻塞渲染并停止HTML的解析。

浏览器构建DOM树时,这个过程占用了主线程。当这种情况发生时,「预加载扫描仪」将解析可用的内容并请求高优先级资源,如CSS、JavaScript和web字体。多亏了预加载扫描器,我们不必等到解析器找到对外部资源的引用来请求它。它将在后台检索资源,以便在主HTML解析器到达请求的资源时,它们可能已经在运行,或者已经被下载。预加载扫描仪提供的优化减少了阻塞。

5.2 样式计算

先有内容,我们才能对内容就行修饰。

样式计算的目的是为了计算出DOM节点中每一个元素的具体样式,这个阶段大体分三步。

5.2.1 把CSS转换为浏览器内容理解的结构

CSS来源有:

  • 外部样式表:通过link引用的CSS文件
  • 内部样式表:style标签内的CSS
  • 内联样式:元素的style属性内嵌的CSS

和HTML文件一样,浏览器也是无法直接理解这些纯文本的CSS样式,所以「当渲染引擎接收到CSS文本的时,会执行一个转换操作,将css文本转换为浏览器可以理解的结构—styleSheets。」

68eeca2d4f17d11a459850c1418e9104.png

渲染引擎会把获取到的 CSS 文本全部转换为 styleSheets 结构中的数据,并且该结构同时具备了查询和修改功能,这会为后面使用JS的样式操作提供基础。

5.2.2 转换样式表中的属性值,使其标准化

我们已经将CSS转换为浏览器能理解的结构了,那么接下来就要对其进行属性值的标准化操作。

那么什么是属性值的标注啊呢?

body { font-size: 2em }
p {color:blue;}
span {display: none}
div {font-weight: bold}
div p {color:green;}
div {color:red; }

可以看到上面的 CSS 文本中有很多属性值,如 2embluebold,这些类型数值不容易被渲染引擎理解,所以需要将所有值转换为渲染引擎容易理解的、标准化的计算值,这个过程就是属性值标准化。

a8a228f2aaefa5111c158afdb3f2ae0d.png

5.2.3 计算出DOM树中每一个节点的具体样式

这里就涉及到CSS「继承规则」「层叠规则」了。

首先是CSS的继承,「css继承是每个DOM节点都包含父节点的样式」。结合以下例子,看下面这张表示如何应用到DOM节点上的。

body { font-size: 20px }

继承规则就是一般文本和字体相关样式都是可以继承的。层叠规则,嵌套的越深权重就越高。

总之,样式计算阶段的目的是为了计算出 DOM 节点中每个元素的具体样式,在计算过程中需要遵守 CSS 的继承和层叠两个规则。这个阶段最终输出的内容是每个 DOM 节点的样式,并被保存在 ComputedStyle 的结构内。

如果你想了解每个 DOM 元素最终的计算样式,可以打开 Chrome 的“开发者工具”,选择第一个“element”标签,然后再选择“Computed”子标签

5.3 布局阶段

现在,我们有DOM树和DOM树中元素的样式,但是还足以显示页面,因为我们还不知道DOM元素的几何位置,那么接下来就需要「计算出DOM树中可见元素的几何位置,我们把这个计算过程叫做布局」

Chrome在布局阶段需要完成两个任务:创建布局树和布局计算

5.3.1 创建布局树

DOM树有些元素不会在页面上显示,被用户看到,如head标签和使用了display:none的元素。所以在显示之前,我么还要额外地构建一棵「只包含了可见元素的布局树」

189c2ac68717c7c4799d1760ad341694.png

从上图可以看出,DOM树中所有不可见的节点都没有有包含到布局树中。

5.3.2 布局计算

我们已经有了一棵完整的布局树,那么接下来就要根据DOM节点对应的CSS树中的样式,计算布局树节点的坐标位置。即计算元素在视口上确切的位置和大小。

5.4 分层 (图层树)

有了布局树之后,每个元素的具体位置信息都计算出来了,那么接下来是不是就要开始着手绘制页面了?不是。因为页面中有很多复杂的效果,如一些「复杂的3D转换」「页面滚动」,或者使用z-index,为了更方便的实现这些效果,「渲染引擎还需要为特定的节点生成专门的图层,并生成一棵对应的图层树(LayerTree)」。这和PS的图层类似,正是这些图层叠加在一起才最终构成了页面图像。

想要直观的理解什么是图层,可以打开Chrome的"开发工具",选择Layers标签,就可以查看可视化页面的分层情况。

「布局树和图层树的关系」

ce835005f30ca6699a5ace4f374223e6.png

通常情况下,并不是布局树中的每一个节点都包含一个图层,如果一个节点没有对应的图层,那么这个节点就从属于父节点的图层。那么什么情况满足,渲染引擎才会为特定的节点创建新的图层呢?满足一下两个条件中的任意一个,元素就可以被单独提升为一个图层。

  1. 「拥有层叠上下文属性的元素会被提升为单独的一层」

页面是一个二维平面,但层叠上下文能够上HTML元素拥有三维概念,这些HTML元素按自身属性的优先级分布在垂直于这个二维平面的Z轴上,以下情况会作为单独的图层。

  • position:fixed
  • css 3d   例如:transform:rotateX(30deg)
  • video
  • canvas
  • CSS3动画的节点
  • will-change
  1. 需要剪裁的地方也会被创建为图层

那么什么是剪裁,结合以下代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        div {
              width: 200px;
              height: 200px;
              overflow:auto;
              background: gray;
          } 
    </style>
</head>
<body>
  <div >
      <p>所以元素有了层叠上下文的属性或者需要被剪裁,那么就会被提升成为单独一层,你可以参看下图:</p>
      <p>从上图我们可以看到,document层上有A和B层,而B层之上又有两个图层。这些图层组织在一起也是一颗树状结构。</p>
      <p>图层树是基于布局树来创建的,为了找出哪些元素需要在哪些层中,渲染引擎会遍历布局树来创建层树(Update LayerTree)。</p> 
  </div>
</body>
</html>

这里我么把div的大小限定为200 * 200像素,而div里面的文字内容比较多,文字所显示的区域肯定会超过200 * 200的面积,这时候就产生了剪裁,渲染引擎会把裁剪文字内容的一部分用于显示在div区域,下面是运行时的执行结果:

e35fbe70345c1f2c54c9faf2a66f733e.png

出现这种裁剪情况时,渲染引擎会为文字单独为文字创建一层,如出现滚动条,滚动条也会被提升为单独的层。

5.5 图层的绘制

在完成图层树的构建之后,渲染引擎会对图层树中的每个图层进行绘制,那么接下来我们看看渲染引擎是如何实现图层的绘制?

如果给你一张纸,让你先把背景涂成暗色,然后再中间中间位置花一个红色的圆,最后在圆上画一个绿色三角,你会怎么操作,通常你会按顺序操作。

渲染引擎实现图层的绘制与之类似,会把一个图层的绘制拆分为很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表,如下图所示:

c808542fc492cb84d3673fd5928542b5.png

从图中可以看出,绘制列表中的指令其实非常简单,就是让其执行一个简单的绘制操作,比如说绘制粉色矩形或者黑色的线等。而绘制一个元素通常需要好几条绘制指令,因为每个元素的背景、前景、边框都需要单独的指令去绘制。所以在「图层绘制阶段,输出的内容就是这些待绘制列表」

5.6 栅格化操作

绘制列表指令用来记录绘制顺序和绘制指令的列表,而实际上「绘制操作是由渲染引擎中的合成线程来完成」。结合下图看渲染主线程和合成线程之间的关系:

5a796a9d1651552f851380a158ae3361.png

如上图所示,当图层的绘制列表准备好之后,主线程会把该绘制列表提交给合成线程,那么合成线程是如何工作的?

首先我们谈一个概念,「视口」。什么是视口?

通常一个页面可能很大,用户只能看到其中的一部分,我们把「用户可以看到的这个区域叫视口(viewport)。」

比如说,一个图层很大,页面需要滚动底部,才能全部显示。但是通过视口,用户只能看到页面很小的一部分,所以在此种情况下,要一次性绘制完图层所有的内容,会产生很大的开销,且没有必要。

基于这个原因,「合成线程会将图层划分为图块」,这些图块的大小通常是256 * 256或512 * 512。然后「合成线程会按照视口附近的图块来优先生成位图」,实际生成位图的操作就是有栅格化来执行的。所谓栅格化,**是指将图块转化为位图(所谓位图就是能够看的到的图层区域)。而图块是栅格化执行的最小单位。**渲染进程维护了一个栅格化的线程池,所有的图块栅格化都是在线程池内执行,运行方式如下图所示:

a4f5b39c0b16fae81b2097ca076f957e.png

通常,栅格化过程都会使用GPU来加速生成,「使用GPU生成位图过程叫快速栅格化,或者GPU栅格化」,生成的位图被保存在GPU内存中。GPU操作是运行在GPU进程中的,那么栅格化,还涉及到了跨进程操作。

f9f9b7d55718fdb5fc4372953c2a54bf.png

从图中可以看出,渲染进程把生成图块的指令发送给 GPU,然后在 GPU 中执行生成图块的位图,并保存在 GPU 的内存中。

5.7 合成和显示

一旦所有图块被栅格化,合成线程就会生成一个绘制图块的命令—“DrawQuad”,然后将该命令提交给浏览器进程。

浏览器进程里有一个叫viz的组件,用来接收合成线程发过来的DrawQuad命令,然后根据DrawQuad命令,将其页面内容绘制到内存中,最后显示在屏幕上。

到此,经过一系列的阶段,编写好的HTMLCSSJavaScript等文件,经过浏览器就会显示为页面。

5.8 总结

我们已经完整分析了整个渲染流程,从HTML到DOM,样式计算,布局,图层,绘制,栅格化,合成和显示。

一个完整的渲染流程大致可总结如下:

  1. 渲染进程将HTML内容转换为浏览器能够读懂的「DOM树」结构。
  2. 渲染引擎将CSS样式表转化为浏览器能够理解的「CSS树」,计算出DOM节点的样式。
  3. DOM树 + CSS树创建布局树,并计算元素的布局信息。
  4. 对布局树进行分层,并生成「图层树」
  5. 对每个「图层」生成「绘制列表」,并将其提交给合成线程。
  6. 对每个图层进行单独的绘制
  7. 合并图层。

6. 相关概念

有了渲染流水线的基础,我们来谈谈和渲染流水线关系的三个概念—「重排」「重绘」「合成」。理解这个三个概念对于后续Web的性能优化会有很大的帮助。

6.1 更新元素的几何属性(重排)

dba173a22dbac4d7caf14c630fb000dd.png

从上图可以看出,如果你「通过JS或CSS修改元素的几何位置属性」,如widthheight等,那么会触发浏览器的重新布局,解析之后的一系列子阶段,这个过程就叫**重排也称回流。「重排需要更新完整的渲染流水线,所以开销也最大的。」

6.2 更新元素的绘制属性(重绘)

比如通过JS更改某些元素的背景颜色,渲染流水的调整参见下图:

257850b6db264477e824966698efd55f.png

修改元素的背景色,布局阶段不会执行,因为「没有引起几何位置的变换」,所以直接进入绘制,然后执行之后的一系列子阶段,这个过程就叫「重绘」。相较重排操作,「重绘省去了布局和分层阶段,所以执行效率会比重排效率高。」

6.3 直接合成阶段

那如果你更改一个既不要布局也不要绘制的属性,渲染引擎将跳过布局和绘制,只执行后续的合成操作,我们把这个过程叫做「合成。」

2d07ec3eec9cca3975266b9a41aeb3e5.png

在上图,我们使用CSStransform来实现动画效果,可以避开重排和重绘阶段,直接在非主线程上执行合成动画操作。这样的效率最高,因为是在非主线程上合成的,并没有占用主线程的资源。

7. 优化方案

如果我们要提升性能,需要做的就是减少浏览器的重绘和回流

  • CSS
  1. 避免使用table布局。
  2. 尽可能在DOM树的最末端改变class。
  3. 避免设置多层内联样式。
  4. 将动画效果应用到position属性为absolute或fixed的元素上。
  5. 避免使用CSS表达式(例如:calc())。
  • JS
  1. 避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。
  2. 避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。
  3. 也可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘。
  4. 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
  5. 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。


相关文章
|
2月前
|
存储 前端开发 开发者
|
1月前
|
前端开发 JavaScript
宏任务和微任务在浏览器渲染过程中的执行顺序
宏任务和微任务是浏览器事件循环中的两种任务类型。宏任务包括整体代码块、setTimeout等,微任务有Promise.then、MutationObserver等。每个宏任务执行完毕后,会先执行完所有微任务,再进行下一轮渲染或执行下一个宏任务。
|
1月前
|
JavaScript 前端开发 API
浏览器渲染过程中如何处理异步任务
在浏览器渲染过程中,异步任务通过事件循环机制处理。JS执行时,同步任务在主线程上执行,形成一个执行栈。异步任务则被推入任务队列中,待主线程空闲时按顺序调用,确保页面流畅渲染与响应。
|
2月前
|
缓存 自然语言处理 前端开发
浏览器渲染
【10月更文挑战第28天】浏览器渲染涉及将HTML、CSS和JavaScript代码转换为可视网页,主要步骤包括:解析HTML构建DOM树、解析CSS构建CSSOM树、合并DOM与CSSOM生成渲染树、布局确定元素位置和尺寸、绘制元素到屏幕、合成图层形成最终图像。此过程不断优化以提升性能。
|
2月前
|
前端开发 JavaScript 异构计算
简述浏览器的渲染原理
浏览器渲染原理主要包括以下步骤:1)解析HTML文档生成DOM树;2)解析CSS生成CSSOM树;3)结合DOM与CSSOM生成渲染树;4)布局计算(回流)确定元素大小和位置;5)绘制(Paint)将节点转为图形内容;6)合成(Composite)多层图像。整个过程从文档解析到最终输出完整网页,并通过优化技术提升性能。
|
7月前
|
Web App开发 JavaScript 前端开发
浏览器与Node.js事件循环:异同点及工作原理
浏览器与Node.js事件循环:异同点及工作原理
|
7月前
|
缓存 JavaScript 前端开发
浏览器渲染:理解页面加载的幕后工作
浏览器渲染:理解页面加载的幕后工作
|
6月前
|
JavaScript 前端开发 网络协议
浏览器的工作原理
主要分为导航、获取数据、HTML解析、css解析、执行javaScript、渲染树几个步骤。
77 1
|
5月前
|
缓存 JavaScript 前端开发
前端 JS 经典:浏览器中 ESModule 的工作原理
前端 JS 经典:浏览器中 ESModule 的工作原理
57 0
|
6月前
|
移动开发 前端开发 JavaScript
浏览器端图表渲染技术SVG, VML HTML Canvas
浏览器端图表渲染技术SVG, VML HTML Canvas
47 0