前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >浏览器工作原理 - 页面

浏览器工作原理 - 页面

作者头像
Cellinlab
发布于 2023-05-17 06:30:49
发布于 2023-05-17 06:30:49
86500
代码可运行
举报
文章被收录于专栏:Cellinlab's BlogCellinlab's Blog
运行总次数:0
代码可运行

Chrome 开发者工具

主要功能面板:

网络面板

网络面板主要有控制器、过滤器、抓图信息、时间线、详细列表和下载信息概要 6 个区域:

  • 控制器
  • 过滤器
    • 筛选名称筛选
    • 按文件类型筛选
  • 抓图信息
    • 分析用户等待页面加载过程中看到的内容,观察用户实际的体验情况
    • 如分析白屏时间
  • 时间线
    • 展示 HTTP、HTTPS、WebSocket 加载的状态和时间的关系,用于直观了解页面的加载过程
    • 如果多条竖线堆叠在一起,说明这些资源被同时加载
  • 详细列表
    • 记录每个资源从发起请求到完成请求所有过程的状态,以及最终请求完成的数据信息
    • 可以方便诊断网络问题
  • 下载信息概要
    • DOMContentLoaded 事件,这个事件发生后,说明页面以及构建好 DOM 了,即构建 DOM 所需要的 HTML 文件、CSS 文件、JS 文件都已经下载完成
    • load 事件,这个事件发生后,说明页面的所有资源都已经加载完成

详细列表

  • 列表的属性
  • 详细信息
  • 单个资源的时间线
  • 时间线面板(Timing)
  • Queuing:浏览器发起请求时,会有很多原因导致请求不能被立即执行,而是需要排队等待
    • 页面中资源有优先级,如 CSS、HTML、JavaScript 等都是页面中的核心文件,优先级高;图片、视频、音频等资源,优先级低。遇到优先级更高的时,进入待排队状态
    • 浏览器会为每个域名最多维护 6 个 TCP 连接,如果发起一个 HTTP 请求时,这 6 个 TCP 连接都处于忙碌状态,请求就会处于排队状态
    • 网络进程在为数据分配磁盘空间时,新的 HTTP 请求也需要短暂地等待磁盘分配的结束
  • Stalled:停滞,如果使用了代理服务器,会增加 Proxy Negotiation(代理协商) 阶段
  • Initial connection / SSL:和服务器建立连接的时间
    • 如果使用了 HTTPS,会增加一个 SSL 握手时间,用于协商一些加密信息
  • Request sent:只需要将浏览器缓冲区的数据发送出去即可,不需要判断服务器是否收到
  • Waiting(TTFB):“第一字节时间”,等待接收服务器第一个字节的数据
    • TTFB 是反映服务端响应速度的重要指标
  • Content Download:从第一字节时间到接收全部响应数据所用的时间

优化时间线上耗时项

  • 排队(Queuing)时间过久
    • 大概率是因为浏览器限制每个域名 6 个 TCP 连接,而导致请求被排队
    • 域名分片技术:可以让 1 个站点的资源放在说个域名下面
    • 升级站点到 HTTP 2:HTTP2 没有每个域名最多维护 6 个 TCP 连接的限制
  • 第一字节时间(TTFB)时间过久
    • 对于动态网页,可能是服务器生成页面数据时间过久
      • 可以通过提高服务器处理速度,如增加各种缓存
    • 网络原因,如低带宽的服务器,或跨网络运营商
      • CDN 缓存静态文件
    • 发送请求时带了多余的信息,服务器处理是可能要对每一项信息做处理
      • 减少携带不必要 Cookie 信息
  • Content Download 时间过久
    • 字节数太多导致,降低文件大小,如压缩、去注释等

DOM 树

什么是 DOM

将网络传给渲染引擎的 HTML 字节流转换为渲染引擎能够理解的内部结构,这种结构就是 DOM,其提供了对 HTML 文档结构化的表述,在渲染引擎中,DOM 有三个层面的作用:

  • 从页面视角来看,DOM 是生成页面的基础数据结构
  • 从 JavaScript 视角看,DOM 提供给 JavaScript 操作的接口,通过这些接口可以对 DOM 进行访问、修改
  • 从安全视角看,DOM 是一道安全防护线,一些不安全的内容在 DOM 解析阶段就被拒之门外了

DOM 树是如何生成的

HTML 解析器(HTML Parser) 负责将 HTML 字节流转换为 DOM 结构。HTML 并不是等整个文档加载完后再解析的,而是 网络进程加载了多少数据,HTML 解析器就解析多少数据

网络进程收到响应头后,根据响应头中 content-type 字段的值,判断文件类型,如果是 text/html 就会为该请求选择或者创建一个渲染进程。然后网络进程和渲染进程之间建立一个共享数据的管道,网络进程接收数据后通过管道将数据传递给渲染进程,交给 HTML 解析器解析。

字节流转换为 DOM 结构的过程,可以分为三个阶段:

  1. 通过分词器将字节流转换为 Token
    • 分 Tag Token 和 Text Token
  2. 将 Token 解析为 DOM 节点,并将 DOM 节点添加到 DOM 树中

HTML 解析器会维护一个 Token 栈结构,用于计算节点之间的赋值关系,在第一阶段中生成的 Token 会被按顺序压入栈中,具体规则如下:

  • 如果压入栈中的是 StartTag Token,HTML 解析器会为该 Token 创建一个 DOM 节点,然后将该节点添加到 DOM 树中,它的父节点就是栈中相邻那个元素生成的节点
  • 如果解析出 Text Token,会生成文本节点,将该节点加入 DOM 树,当 Text Token 不进栈,它的父亲节点就是当前栈顶 Token 对应的 DOM 节点
  • 如果解析出 EndTag Token,如 EndTag div,HTML 解析器检查栈顶元素是否是 StartTag div,如果是,则将栈顶元素弹出,表示该 div 元素解析完成

通过分词器产生的新 Token 不停进栈和出栈,整个解析过程一直持续,直到分词器将所有字节流分词完成。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<html>
  <body>
    <div>1</div>
    <div>test</div>
  </body>
</html>

复制🤏

HTML 解析器开始工作时,会默认创建一个根为 document 的空 DOM 结构,同时将一个 StartTag document 的 Token 压入栈中,然后经过分词器处理,解析出第一个 StartTag html Token,将其压入栈中,并创建一个 html 的 DOM 节点,添加到 document 上:

然后依次解析 body 和 div:

当解析出 Text Token 时,渲染引擎会为 Text Token 创建一个文本节点,并将其添加到 DOM 树中:

当解析出 EndTag div 时,HTML 解析器会去判断当前栈顶元素是否是 StartTag div,如果是,则从栈顶弹出 StartTag div:

最终解析结果如下:

JavaScript 是如何影响 DOM 生成的

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<html>
  <body>
    <div>1</div>
    <script>
      let div1 = document.querySelector('div')[0];
      div1.innerHTML = 'cellinlab';
    </script>
  </body>
</html>

当解析到 <script> 标签时,渲染引擎判断是脚本,会暂停 DOM 的解析,因为 JavaScript 可能会修改当前已经生成的 DOM 结构:

HTML 解析器暂停工作后,JavaScript 引擎会介入,并执行 script 标签中的脚本,将 DOM 节点中内容进行修改,脚本执行完后,HTML 解析器恢复解析过程,继续解析后续内容。

如果 JavaScript 是引入的,在执行脚本之前,还需要去下载,由于下载会阻塞 DOM 解析。Chrome 做了一些优化,主要优化是预解析,当渲染引擎收到字节流后,会开启一个预解析线程,用来分析 HTML 文件中包含的 JavaScript、CSS 等相关文件,解析到相关文件后,预解析线程会提前下载这些文件。

可以通过 使用 CDN 、压缩文件大小等方法来加速 JavaScript 的加载,另外,如果 JavaScript 中没有操作 DOM 相关代码,可以设置异步加载,通过 asyncdefer 属性来实现:

  • async 标志的文件,一旦加载完,会立即执行
  • defer 标志的文件,需要在 DOMContentLoaded 事件之前执行

渲染流水线

流水下视角下的 CSS

theme.css

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
div {
  color: coral;
  background-color: black;
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<html>
  <head>
    <link href="theme.css" rel="stylesheet" >
  </head>
  <body>
    <div>cellinlab</div>
  </body>
</html>

打开文件时,HTML 文件渲染流水线大致如下:

CSSOM

和 HTML 一样,渲染引擎也是无法直接理解 CSS 文件内容的,所以需要将其解析成渲染引擎能够理解的结构 —— CSSOM(CSSOM 体现在 DOM 中就是 document.styleSheets),主要有两个作用:

  • 提供给 JavaScript 操作样式表的能力
  • 为布局树的合成提供基础的样式信息

等 DOM 和 CSSOM 都构建好之后,渲染引擎就会构造布局树:

  • 布局树的结构基本上就是复制 DOM 树的结构,不过会过滤不显示元素,如 display: none 元素、head 标签、script 标签等
  • 样式计算:复制好基本的布局树结构之后,渲染引擎会为对应的 DOM 元素选择对应的样式信息
  • 计算布局:样式计算完成后,渲染引擎还需要计算布局树中每个元素对应几何位置
  • 通过样式计算和计算布局就完成了最终布局树的构建,再之后就该进行绘制操作

对于一些复杂的场景,如: theme.css

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
div {
  color: coral;
  background-color: black;
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<html>
  <head>
    <link href="theme.css" rel="stylesheet" >
  </head>
  <body>
    <div>cellinlab</div>
    <script>
      console.log('cellinlab.xyz');
    </script>
    <div>cellinlab.xyz</div>
  </body>
</html>

由于增加了 JavaScript,渲染流水线会发生一些变化:

在解析 DOM 过程中,如果遇到 JavaScript 脚本,会暂停 DOM 解析去执行 JavaScript,因为 JavaScript 可能会修改当前状态下的 DOM。不过,如果在执行 JavaScript 脚本前,页面中包含了外部 CSS 文件的引用,或者通过 style 标签内置了 CSS 内容,那么渲染引擎还需要将这些内容转换为 CSSOM,因为 JavaScript 有修改 CSSOM 的能力,所以在执行 JavaScript 前,还需要依赖 CSSOM,即 CSS 在部分情况下也会阻塞 DOM 生成

如果 body 中包含 JavaScript 外部引用文件,会让情况变得更加复杂: theme.css

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
div {
  color: coral;
  background-color: black;
}

foo.js

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
console.log('cellinlab.xyz');
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<html>
  <head>
    <link href="theme.css" rel="stylesheet" >
  </head>
  <body>
    <div>cellinlab</div>
    <script src="foo.js"></script>
    <div>cellinlab.xyz</div>
  </body>
</html>

影响页面展示的因素及优化策略

渲染流水线影响首次页面展示的速度,首次页面展示速度会直接影响用户体验。通过分析影响首屏展示因素,可以针对性做出优化:

  • 可以分为三个阶段
    • 请求发出,到提交数据阶段,页面展示之前内容
    • 提交数据之后渲染进程会创建空白页面(解析白屏),并等待 CSS 和 JavaScript 文件加载完成,生成 CSSOM 和 COM,然后合成布局树,再经过系列处理准备首次渲染
    • 首次渲染完成后,进入完整页面的生成阶段,页面会一点一点被绘制出来
  • 第一阶段影响因素
    • 网络
    • 服务器处理
  • 第二阶段主要问题是 白屏时间,主要任务有:
    • 解析 HTML
    • 下载 CSS
    • 下载 JavaScript
    • 合成 CSSOM
    • 执行 JavaScript
    • 生成布局树
    • 绘制页面
  • 缩短白屏时间的策略
    • 通过内联 JavaScript 和 CSS 减少这两种类型文件下载,获取到 HTML 文件后可以直接开始渲染
    • 在不适合内联的场景,尽可能减小文件大小,如 webpack 移除注释,压缩代码
    • 可以将不需要在解析 HTML 阶段使用的 JavaScript 标记 asyncdefer
    • 对于大的 CSS 文件,通过媒体查询属性,将其拆分为不同用途 CSS 文件,在特定场合再加载

分层和合成机制

图像显示原理

显示器有固定的刷新频率,通常是 60Hz,可以理解为每秒渲染 60 次,更新前的内容都来自于显卡中的前缓存区。显示器做的任务很简单,就是每秒固定读取 60 次缓存区图像,显示到显示器上。

显卡负责合成新图像,并将图像保存到后缓存区中,一旦显卡将合成图像写到后缓冲区,系统就会让后缓冲区和前缓冲区互换,这样能保证显示器能读取到最新显卡合成的图像。通常,显卡的更新频率和显示器一致,有时,在复杂场景,显卡处理速度变慢,会造成视觉上的卡顿。

帧和帧率

将渲染流水线生成的每一幅图片称为一,把流水线每秒更新了多少帧称为帧率。如,滚动页面过程中,1 秒更新了 60 帧,那帧率就是 60 Hz(或 60 FPS)。

由于用户很容易观察到那些丢失的帧,如果在一次动画过程中,渲染引擎生成某些帧的时间过久,那么用户就会感受到卡顿,会造成不好的用户体验。

要解决卡顿问题,就要解决每帧生成时间过久的问题,为此 Chrome 对浏览器渲染方式做了一些优化,最有成效的策略就是分层合成机制,这代表了当前最先进的渲染技术。

如何生成帧图像

  • 任意一帧的生产方式有: 重排重绘合成
  • 三种方式渲染路径不同,通常渲染路径越长,生成图像花费的时间就越多
    • 重排,需要重新根据 CSSOM 和 DOM 来计算布局树,这样生成一幅图会让整个渲染流程走一遍,布局复杂的话,很难保证渲染的效率
    • 重绘因为没有了重新布局的阶段,操作效率稍微高一点,但依然需要计算绘制信息,并触发绘制操作之后的一系列操作
    • 合成的操作路径较短,并不需要触发布局和绘制两个阶段,如果采用了 GPU,合成效率会非常高

分层和合成

通常页面组成是非常复杂的,如果没有分层机制,会“牵一发而动全身”严重影响页面渲染效率。为了提升每帧的渲染效率,Chrome 引入了分层和合成的机制

  • 分层:将素材分解为多个图层
  • 合成:将多个图层合成为一幅图像

分层和合成通常一起使用。如,一个页面被分为两层,当进行下一帧的渲染时,前一帧可能需要实现某些变换(平移、缩放、阴影等),此时合成器只需要将两个层进行相应处理,显卡处理这些操作很容易,这样合成过程时间就非常短了。

分层和合成的具体实现:

  • 在 Chrome 的渲染流水线中,分层体现在生成布局树之后,渲染引擎会根据布局树的特点将其转换为层树(Layer Tree),层树是渲染流水线后续流程的基础结构
  • 层树中的每个节点都对应一个图层,下一步绘制阶段就依赖于层树中的节点
  • 绘制阶段其实并不是真正地绘出图片,而是将绘制指令组合成一个列表
  • 有了绘制列表后,就需要进入光栅化阶段,光栅化就是按照绘制列表中的指令生成图片
  • 每一个图层都对应一张图片,合成线程有了这些图片之后,会将这些图片合成为“一张”图片,并最终将生成的图片发送到后缓冲区
  • 合成操作是在合成线程上完成的,即执行合成操作时,是不会影响到主线程执行的(这也是为什么主线程卡主时,CSS 动画依然能执行的原因)

分块

分层从宏观上提升了渲染效率,分块则是从微观层面提升了渲染效率

  • 通常,页面内容比屏幕大,显示页面时等待所有图层都生成完毕再进行合成,会产生一些不必要的开销,也会让合成图片的时间变久
    • 为了提高效率,合成线程将每个图层分割成块,优先绘制靠近视口的图块,这样可以提高页面显示速度
  • 但有时即使只绘制优先级较高的图块,也要耗费不少时间,因为涉及纹理上传,从计算机内存上传到 GPU 内存的操作会比较慢
    • Chrome 采取首次合成图块时使用一个低分辨率的图片来减少纹理,提高渲染效率

利用分层技术优化代码

在于元素的几何形状变换、透明度或者缩放操作,如果使用 JavaScript 来写,会牵涉整个渲染流水线,所以 JavaScript 的绘制效率会非常低下。此时,可以使用 will-change 来告知渲染引擎会对该元素进行一些变换,渲染引擎会将该元素单独实现一帧,等这些变换发生时,渲染引擎会通过合成线程去直接处理变换,这些变换不会涉及到主线程,所以效率会提高。这也是 CSS 动画比 JavaScript 动画高效的原因

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
.box {
  will-change: transform, opacity;
}

当然,每当渲染引擎为一个元素准备一个独立图层的时候,它占用的内存也会大大增加,因为从层树开始,后续每个阶段都会多一个层结构,都会消耗内存,所以需要恰当地使用 will-change

页面性能

主要关于如何让页面更快地显示和响应,一个页面通常分为三个阶段:

  • 加载阶段:发出请求到渲染出完整页面的过程,影响因素有网络和 JavaScript 脚本
  • 交互阶段:页面加载完成到用户交互的整个过程,影响因素主要是 JavaScript 脚本
  • 关闭阶段:用户发出关闭指令后页面所做的一些清理操作

加载阶段

典型的渲染流水线:

将能阻塞网页首次渲染的资源称为关键资源,如 JavaScript、首次请求的 HTML、CSS 等。对于关键资源,影响页面首次渲染的因素有:

  • 关键资源个数
    • 关键资源个数越多,首次页面加载时间就越长
  • 关键资源大小
    • 关键资源内容越小,下载时间越短,阻塞渲染的时间就越短
  • 请求关键资源需要多少 RTT (Round Trip Time)
    • RTT 指往返时延,是网络中一个重要的性能指标,表示从发送端发送数据开始,到发送端收到来自接收端的确认,总共经历的时延
    • 通常 1 个 HTTP 数据包大小再 14KB 左右,如 1 个 0.1 M 的页面需要拆分 8 个包来传输,就需要 8 个 RTT

针对以上情况,主要优化原则就是减少关键资源个数,降低关键资源大小,减少关键资源的 RTT 次数

  • 减少关键资源个数
    • 将 JavaScript 和 CSS 改成内联模式
    • JavaScript 如果没有 DOM 或 CSSOM 操作,可以设置 asyncdefer 标记
    • CSS 如果不是在构建页面之前加载,可以添加媒体取消阻止显现的标志
  • 降低关键资源大小
    • 压缩代码
    • 移除注释
  • 减少关键资源的 RTT 次数
    • 使用 CDN

交互阶段

交互阶段的优化,实际是关于渲染进程渲染帧的速度,在交互阶段,帧的渲染速度决定交互的流畅度。交互阶段的渲染流水线,没有了加载关键资源和构建 DOM 、CSSOM 流程,通常由 JavaScript 触发交互动画:

大部分情况下,生成一个新的帧是由 JavaScript 通过修改 DOM 或者 CSSOM 来触发的,还有一部分帧是由 CSS 来触发的。

如果在计算样式阶段发现有布局信息的修改,就会触发重排,然后触发渲染流水线系列操作,代价很大。

同样,如果在计算样式阶段发现没有布局信息修改,只是修改颜色一类信息,不涉及布局相关,就会跳过布局阶段,进入绘制阶段,这个过程叫重绘,也会有不小的代价。

还有一种情况,通过 CSS 实现一些变形、渐变、动画等特效,这由 CSS 触发,并且在合成线程上执行,这个过程叫合成。因为不会触发重排或重绘,而且合成操作速度很快,所以执行合成时效率最高的方式。

综上,在交互过程中,优化的主要原则就是让单个帧的生成速度变快,可以从下面入手解决:

  • 减少 JavaScript 执行时间
    • 将一次执行的函数分解为多个任务,使得每次执行时间不要过久
    • 采用 Web Workers,将任务放到主线程外的线程,在 Web Workers 可以执行 JavaScript 脚本,不过不能访问 DOM、CSSOM
  • 避免强制同步布局
    • 强制同步布局:JavaScript 强制将计算样式和布局操作提前到当前的任务中。如,查询元素 offsetWidthoffsetHeight
    • 为了避免强制同步布局,可以在修改 DOM 之前进行相关值的查询
  • 避免布局抖动
    • 布局抖动:指在一次 JavaScript 执行过程中,多次执行强制布局和抖动操作
    • 尽量不要在修改 DOM 结构时,再去查询一些相关的值
  • 合理利用 CSS 动画
    • 合成动画是直接在合成线程上执行的,如果主线程被 JavaScript 或 一些布局任务占用,CSS 动画依然能继续执行
    • 要尽量利用好 CSS 合成动画,如果能让 CSS 处理动画,就尽量交给 CSS 来操作
    • 如果知道某个元素将来可能执行动画操作,也可以通过标记 will-change 将元素抽取单独图层,提高渲染效率
  • 避免频繁的垃圾回收
    • JavaScript 使用自动垃圾回收机制,如果函数中频繁创建临时对象,那么垃圾回收器会频繁执行垃圾回收策略
    • 垃圾回收会占用主线程,从而影响其他任务的执行,严重的话会让用户产生掉帧、不流畅的感觉
    • 可以尽可能优化存储结构,尽可能避免小颗粒对象的产生

虚拟 DOM

DOM 的缺陷

通过 JavaScript 操作 DOM 会影响到整个渲染流水线。如,document.body.appendChild(node) 会发生一系列连锁反应,这些操作都会降低渲染效率:

  1. 渲染引擎会将 node 节点添加到 body 节点之上,之后会触发样式计算、布局、绘制、栅格化、合成等任务(即重排)
  2. 除了重排外,还可能引起重绘合成操作
  3. 对于 DOM 的操作不当还可能引发强制同步布局布局抖动

对于简单页面,可以以上操作可能问题不太明显。但是对于一些复杂的页面和项目,DOM 结构非常复杂,而且可能需要不断去修改 DOM 树,每次操作 DOM 渲染引擎都需要进行重排、重绘或合成等操作,由于页面和 DOM 复杂,这些操作会很耗时,带来很大的性能问题。

需要一种方法来减少 JavaScript 对 DOM 的操作,所以有了虚拟 DOM。

什么是虚拟 DOM

虚拟 DOM 要解决的问题:

  • 将页面改变的内容应用到虚拟 DOM 上,而不是直接应用到 DOM 上
  • 变化被应用到虚拟 DOM 上时,虚拟 DOM 并不立刻去渲染页面,而仅仅是调整虚拟 DOM 的内部状态,这样操作虚拟 DOM 的代价就变小了
  • 在虚拟 DOM 收集到足够的改变时,再将这些变化一次性应用到真实的 DOM 上

虚拟 DOM 的运行过程:

  • 创建阶段
    • 首先依据 JSX 和基础数据创建出来虚拟 DOM,它反映真实的 DOM 树的结构
    • 然后由虚拟 DOM 创建出真实 的 DOM 树,真实的 DOM 树生成完成后,再触发渲染流水线往屏幕输出页面
  • 更新阶段
    • 如果数据发生了改变,那么就需要根据新的数据创建一个新的虚拟 DOM 树
    • 然后比较两个树,找出变化的地方,并把变化的地方一次性更新到真实的 DOM 树上
    • 最后渲染引擎更新渲染流水线,并生成新的页面

从双缓存和 MVC 模型看虚拟 DOM:

  • 双缓存
    • 在开发游戏或处理其他图像的过程中,屏幕从前缓冲区读取数据后显示,但是一些计算较复杂的情况,可能会缓存跟不上显示,所以可以启用双缓存,将计算结果提前缓存到另一个缓存区
    • 某种程度上,可以将虚拟 DOM 看做 DOM 的一个 Buffer,在完成一次完整操作后再把结果应用到 DOM 上,减少不必要的更新,同时还能保证 DOM 稳定输出
  • MVC
    • MVC 基础结构由模型、视图和控制器组成,核心是将数据和视图分离,基于它又衍生了 MVP、MVVM 等
    • React 和 Vue 都是 MVC 结构骨架
    • 可以将虚拟 DOM 看成 MVC 的视图部分,其控制器和模型都是由 Redux 提供的
      • 控制器监控 DOM 变化,一旦 DOM 发生变化,控制器就会通知模型,让其更新数据
      • 模型数据更新好后,控制器会通知视图,告诉它模型的数据发生了变化
      • 视图接收更新消息后,会根据模型所提供的数据来生成新的虚拟 DOM
      • 新的虚拟 DOM 生成好之后,需要与之前的虚拟 DOM 进行比较,找出变化的节点
      • 比较出变化的节点后,React 将变化的虚拟节点应用到 DOM 上,这样就会触发 DOM 节点的更新
      • DOM 节点的变化触发后续系列渲染流水线的变化,从而实现页面的更新

PWA

PWA(Progressive Web App),渐进式网页应用:

  • 在 Web 应用开发者角度
    • PWA 提供一个渐进式的过渡方案,让普通站点逐步过渡到 Web 应用
    • 使用渐进式可以降低站点改造成本,使站点逐步支持各项新技术
  • 在技术角度看
    • PWA 是一个渐进式的演化过程,在技术层面会一点点演进

PWA 采用一个缓和的渐进式策略,不是直接取代本地 APP、取代小程序,而是充分发挥 Web 的优势,渐进式缩短和本地应用或小程序之间的距离。

Web 最大的优势可以说是自由开放,因为自由开发,大家很容易对同一件事达成共识,达成共识后,一套代码就可以在各种设备上运行了,即跨平台。这是本地应用所不具备的。

可以说,PWA 是一套理念,渐进式增强 Web 的优势,并通过技术手段渐进式缩短和本地应用或者小程序之间的距离。

Web 应用 VS 本地应用

相对于本地应用,Web 页面缺少一些能力:

  • 缺少离线使用的能力,在离线或者弱网环境下基本上是无法使用的
  • 缺少消息推送(原生)的能力
  • 缺少一级入口,即不能将 Web 应用安装到桌面,需要通过浏览器打开

PWA 提出的解决方案是:

  • 引入 Service Worker 尝试解决离线存储和消息推送的问题
  • 引入 manifest.json 解决一级入口问题

Service Worker

Service Worker 思路是在页面和网络之间增加一个拦截器,来缓存和拦截请求

Service Worker 设计思路

  • 架构
    • 参考 Web Worker 的思路,运行在主线程之外,不过 Web Worker 是临时的,执行完会退出,结果不能保存,Service Worker 在 Web Worker 之上加了存储功能
    • Service Worker 会给多个页面提供服务,不能和单个页面绑定起来,因为其运行在浏览器进程(浏览器进程生命周期最长),所以在浏览器生命周期内,能为所有页面提供服务
  • 消息推送
    • 消息推送时,页面可能没有启动,需要 Service Worker 来接收消息,通过一定方式展示给用户
  • 安全
    • 除了 Web 默认的安全策略,还需要使用 HTTPS

Web Component

组件化的核心是对内高内聚,对外低耦合。对内各个元素彼此紧密结合、相互依赖,对外和其他组件联系最少且接口简单。

阻碍前端组件化的因素

  • CSS 的全局属性
  • 页面中只有一个 DOM,任何地方都可以直接读取和修改 DOM

Web Component 组件化开发

Web Component 的思路是提供对局部视图封装能力,可以让 DOM、CSSOM 和 JavaScript 运行在局部环境中,使得局部的 CSS 和 DOM 不会影响到全局。

Web Component (opens new window) 是一套技术的组合,涉及 Custom Element、 Shadow DOM 和 HTML Template。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!DOCTYPE html>
<html>
  <body>
    <!-- 定义模板 -->
    <template id="my-tpl">
      <!-- 定义内部 CSS 样式  -->
      <style>
        p {
          background: brown;
          color: cornsilk;
        }
        div {
          width: 200px;
          background-color: bisque;
          border: 3px solid chocolate;
          border-radius: 10px;
        }
      </style>
      <!-- 定义内部 HTML -->
      <div>
        <p>Hello World</p>
      </div>
      <!-- 定义 JavaScript 行为 -->
      <script>
        function sayHello() {
          console.log('Hello World');
        }
      </script>
    </template>
    <script>
      // 定义组件
      class MyComponent extends HTMLElement {
        constructor() {
          super();
          
          // 获取组件模板
          const template = document.querySelector('#my-tpl').content;
          // 创建 影子 DOM 节点
          const shadow = this.attachShadow({ mode: 'open' });
          // 将模板插入到影子 DOM 节点
          shadow.appendChild(template.cloneNode(true));
        }
      }
      // 注册组件
      customElements.define('my-component', MyComponent);
    </script>
    <my-component></my-component>
  </body>
</html>

使用 Web Component 的三个步骤:

  1. 使用 template 标签创建模板
    • 利用 DOM 可以查看到 template 的内容,当 template 元素不会被渲染到页面,即不会出现在布局树
    • 一般,还需要在模板内部定义样式信息
  2. 创建一个 MyComponent 类
    • 该类的构造函数中完成三件事
      • 查找模板内容
      • 创建影子 DOM
      • 将模板内容插入到影子 DOM
    • 影子 DOM 是将模板中的内容与全局 DOM 和 CSS 进行隔离,实现元素和样式的私有化
      • 可以将影子 DOM 看做一个作用域,内部样式和元素不会影响到全局的样式和元素
      • 在全局环境下,要访问影子 DOM 内部的样式或者元素需要通过约定好的接口
  3. 在 HTML 中使用组件

浏览器如何实现影子 DOM

影子 DOM 的作用:

  • 对于整个网页是不可见的
  • 其中的 CSS 不会影响整个网页的 CSSOM

影子 DOM 的实现:

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/5/13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
浏览器原理学习笔记05—浏览器中的页面渲染
DOM 是表述 HTML 的内部数据结构,它会将 Web 页面和 JavaScript 脚本连接起来,并过滤一些不安全的内容。HTML 解析器 (HTMLParser) 模块负责将 HTML 字节流转换为 DOM 结构。网络进程接收到响应头后会根据响应头中的 content-type 字段来判断文件的类型,若为 text/html,则为该请求创建一个渲染进程。渲染进程准备好后,网络进程和渲染进程之间会建立一个共享数据的管道,HTML 解析器并不是等整个文档加载完成之后再解析,而是网络进程加载了多少数据,HTML 解析器便解析多少数据。
CS逍遥剑仙
2020/05/02
1.5K0
阶段五:浏览器中的页面
21 | Chrome开发者工具:利用网络面板做性能分析 页面是浏览器的核心,浏览器中的所有功能都是服务于页面的,Chrome开发者工具又是调试页面的核心工具。 网络面板 控制器 开始或停止抓包 全局搜索 禁止从cache中加载资源 模拟网络 过滤器 抓图信息:Capture screenshots 详细列表:重点内容 下载信息概要 DOMContentLoaded:页面已经构建好DOM,所需要的HTML、CSS和JS文件都已经下载完成 Load:浏览器已经加载了所有的资源(图片、样式表等) 详
六个周
2022/10/28
8930
画了20张图,详解浏览器渲染引擎工作原理
通常,我们编写的HTML、CSS、JavaScript等文件,经过浏览器运行之后就会显示出页面,那他们是如何转化为页面的?这背后的原理是什么?这个过程就是浏览器的渲染进程来操作实现的。浏览器的渲染进程的主要任务就是「将静态资源转化为可视化界面:」
HoMeTown
2022/10/26
2.7K0
画了20张图,详解浏览器渲染引擎工作原理
浏览器工作原理 - 浏览器整体概览
可以通过 Chrome 浏览器中的 选项->更多工具->任务管理器,打开 Chrome 的任务管理器窗口,来查看 Chrome 打开一个页面,需要启动多少进程:
Cellinlab
2023/05/17
7380
浏览器工作原理 - 浏览器整体概览
浏览器渲染之回流重绘
回流和重绘是前端开发的高频词汇之一,你可以在各种面经,性能优化相关文章可以看到,但是很多都是草草带过。本文带你从浏览器渲染流程中了解回流与重绘的原理。
政采云前端团队
2021/09/30
1.7K0
浏览器渲染之回流重绘
作者学习完《浏览器基本原理与实践》后的 36 点总结
作为一名前端er,日常工作打交道最多(之一)的莫过于熟悉而又陌生的浏览器了,熟悉是每天都会基于浏览器的应用层面之上码业务,陌生是很多人可能跟我一样不熟悉其内部运行原理,比如js是怎样运行的呢?精美样式页面是怎样渲染到电脑屏幕的呢?在开放的互联网它又是怎样保证我们个人信息安全的呢?带着种种疑云开始肝李兵老师的《浏览器基本原理与实践》,不得不说,大家之作,通俗易懂,层层拨开云雾见青天,下面就(非常非常)简单总结一下。
五月君
2021/11/30
1.1K0
浏览器渲染原理
「进程 :」 进程是操作系统资源分配的基本单位,进程中包含线程。简而言之,就是正在进行中的应用程序。
用户8921923
2022/10/24
1.1K0
浏览器渲染原理
浏览器工作原理
有许多浏览器正在被使用,截至2022年,使用最多的是:谷歌浏览器、苹果的Safari、微软的Edge和火狐。
can4hou6joeng4
2023/11/30
2870
浏览器渲染(线程视角1)
上一篇 浏览器渲染(进程视角)文章从浏览器的进程模型演进分析了打开一个页面的渲染进程数量,及每个渲染页面的连接,上下文组等内容,那么对于渲染进程内所作的事情怎样的呢?
醉酒鞭名马
2020/05/23
2.4K3
浏览器渲染(线程视角1)
聊一聊前端性能优化 CRP
优化关键渲染路径可以提升首屏渲染时间。理解和优化关键渲染路径对于确保回流和重绘可以每秒 60 帧、确保高性能的用户交互和避免无意义渲染至关重要。
前端森林
2020/07/09
9250
浏览器渲染流程(上)
浏览器渲染流程(上) 前言 看面试题查漏补缺中,不太熟悉的点重新学习,输出文章,备战秋招。 这篇文章是讲浏览器怎么渲染出页面的。 顺带一提:本文有一些很好的过程图,是从参考文章里引用过来的,并且参考文章的图好像又是极客时间的李兵老师的视频里的。(😅) 渲染流程 解析HTML,生成DOM树 解析CSS,生成CSSOM树 布局(Layout) 结合DOM树和CSSOM树,生成渲染树 布局计算 分层(Layer) 绘制(Paint) 合成(\color{red}上面部分是在
赤蓝紫
2023/03/16
3710
浏览器渲染流程(上)
最详尽的浏览器页面渲染机制分析
浏览器的内核是指支持浏览器运行的最核心的程序,分为两个部分的,一是渲染引擎,另一个是JS引擎。渲染引擎在不同的浏览器中也不是都相同的。目前市面上常见的浏览器内核可以分为这四种:Trident(IE)、Gecko(火狐)、Blink(Chrome、Opera)、Webkit(Safari)。这里面大家最耳熟能详的可能就是 Webkit 内核了,Webkit 内核是当下浏览器世界真正的霸主。本文我们就以 Webkit 为例,对现代浏览器的渲染过程进行一个深度的剖析。
挨踢小子部落阁
2019/12/04
1.6K0
五分钟了解浏览器工作原理
Web 浏览器无疑是用户访问互联网最常见的入口。浏览器凭借其免安装和跨平台等优势,逐渐取代了很多传统的富客户端。
音视频开发进阶
2020/05/26
9490
五分钟了解浏览器工作原理
vue在浏览器中对DOM渲染探究
“世间万物都由分子构成。用气将万物的分子打散,分解眼前事物,再将分子重组,在短暂的瞬间,可以凝成时空停顿,甚至逆转时空。”
Yerik
2022/04/06
1.2K0
浏览器渲染原理
浏览器收到的其实就是HTML文件,只有HTML格式浏览器才能正确解析。接下来就是浏览器的渲染过程。
Cloud-Cloudys
2020/07/06
1K0
浏览器渲染原理及流程
大多数设备的刷新频率是60Hz,也就说是浏览器对每一帧画面的渲染工作要在16ms内完成,超出这个时间,页面的渲染就会出现卡顿现象,影响用户体验。前端的用户体验给了前端直观的印象,因此对B/S架构的开发人员来说,熟悉浏览器的内部执行原理显得尤为重要。
前端迷
2019/08/29
4.6K0
浏览器渲染原理及流程
带你了解浏览器工作过程
每启动一个应用程序,操作系统都会为此程序创建一块内存,用来存放代码、数据数据、一个执行任务的主线程,我们把这样的一个运行环境叫进程。
大发明家
2021/12/21
1.7K0
窥探现代浏览器架构(三)
本文是笔者对Mario Kosaka写的inside look at modern web browser系列文章的翻译。这里的翻译不是指直译,而是结合个人的理解将作者想表达的意思表达出来,而且会尽量补充一些相关的内容来帮助大家更好地理解。
进击的大葱
2022/08/22
5320
窥探现代浏览器架构(三)
浏览器层面优化前端性能(2):Reader引擎线程与模块分析优化点
renderer与DOM元素是相对应的,但并不是一一对应,有些DOM元素没有对应的renderer,而有些DOM元素却对应了好几个renderer,对应多个renderer的情况是普遍存在的,就是为了解决一个renderer描述不清楚如何显示出来的问题,譬如有下拉列表的select元素,我们就需要三个renderer:一个用于显示区域,一个用于下拉列表框,还有一个用于按钮。
周陆军博客
2023/04/09
1.2K0
一文看懂Chrome浏览器工作原理
本文是笔者对Mario Kosaka写的inside look at modern web browser系列文章的翻译。这里的翻译不是指直译,而是结合个人的理解将作者想表达的意思表达出来,而且会尽量补充一些相关的内容来帮助大家更好地理解。
进击的大葱
2022/08/22
2.2K0
一文看懂Chrome浏览器工作原理
相关推荐
浏览器原理学习笔记05—浏览器中的页面渲染
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文