DOM树:JavaScript是如何影响DOM树构建的?
-
什么是 DOM
从网络传给渲染引擎的 HTML 文件字节流是无法直接被渲染引擎理解的,所以要将其转化为渲染引擎能够理解的内部结构,这个结构就是 DOM。
DOM 是表述 HTML 的内部数据结构,它会将 Web 页面和 JavaScript 脚本连接起来,并过滤一些不安全的内容。 -
DOM 树如何生成
HTML 解析器并不是等整个文档加载完成之后再解析的,而是网络进程加载了多少数据,HTML 解析器便解析多少数据。
代码从网络传输过来是字节流的形式,字节流转换为 DOM 需要三个阶段。- 通过分词器将字节流转换为 Token
- 将 Token 解析为 DOM 节点,并将 DOM 节点添加到 DOM 树中
-
JavaScript 是如何影响 DOM 生成的
解析到 <script> 标签时,渲染引擎判断这是一段脚本,此时 HTML 解析器就会暂停 DOM 的解析,因为接下来的 JavaScript 可能要修改当前已经生成的 DOM 结构。脚本执行完成之后,HTML 解析器恢复解析过程,继续解析后续的内容,直至生成最终的 DOM。
渲染引擎在遇到 JavaScript 脚本时,不管该脚本是否操纵了 CSSOM,都会执行 CSS 文件下载,解析操作,再执行 JavaScript 脚本。
JavaScript 会阻塞 DOM 生成,而样式文件又会阻塞 JavaScript 的执行。
渲染流水线:CSS如何影响首次加载时的白屏时间?
- 渲染流水线视角下的 CSS
含有 JavaScript 文件和 CSS 文件页面的渲染流水线
- 影响页面展示的因素以及优化策略
渲染流水线影响到了首次页面展示的速度,而首次页面展示的速度又直接影响到了用户体验。
提交数据之后渲染进程会创建一个空白页面,我们通常把这段时间称为解析白屏,这个阶段的主要任务,包括了解析 HTML、下载 CSS、下载 JavaScript、生成 CSSOM、执行 JavaScript、生成布局树、绘制页面一系列操作。通常情况下的瓶颈主要体现在下载 CSS 文件、下载 JavaScript 文件和执行 JavaScript。
页面性能:如何系统地优化页面?
页面优化,其实就是要让页面更快地显示和响应。通常一个页面有三个阶段:加载阶段、交互阶段和关闭阶段。
- 加载阶段,是指从发出请求到渲染出完整页面的过程,影响到这个阶段的主要因素有网络和 JavaScript 脚本。
- 交互阶段,主要是从页面加载完成到用户交互的整合过程,影响到这个阶段的主要因素是 JavaScript 脚本。
- 关闭阶段,主要是用户发出关闭指令后页面所做的一些清理操作。
-
加载阶段
JavaScript、首次请求的 HTML 资源文件、CSS 文件是会阻塞首次渲染的,因为在构建 DOM 的过程中需要 HTML 和 JavaScript 文件,在构造渲染树的过程中需要用到 CSS 文件。这些能阻塞网页首次渲染的资源称为关键资源。
总的优化原则就是减少关键资源个数,降低关键资源大小,降低关键资源的 RTT(Round Trip Time,RTT 就是这里的往返时延。它是网络中一个重要的性能指标,表示从发送端发送数据开始,到发送端收到来自接收端的确认,总共经历的时延。) 次数。- 将 JavaScript 和 CSS 改成内联的形式
- 如果 JavaScript 代码没有 DOM 或者 CSSOM 的操作,则可以改成 sync 或者 defer 属性;对于 CSS,如果不是在构建页面之前加载的,则可以添加媒体取消阻止显现的标志。当 JavaScript 标签加上了 sync 或者 defer、CSSlink 属性之前加上了取消阻止显现的标志后,它们就变成了非关键资源了。
- 压缩 CSS 和 JavaScript 资源,移除 HTML、CSS、JavaScript 文件中一些注释内容
- 使用 CDN 来减少每次 RTT 时长
-
交互阶段
帧的渲染速度决定了交互的流畅度。因此讨论页面优化实际上就是讨论渲染引擎是如何渲染帧的,否则就无法优化帧率。一个大的原则就是让单个帧的生成速度变快。-
减少 JavaScript 脚本执行时间。一种是将一次执行的函数分解为多个任务,使得每次的执行时间不要过久;另一种是把一些和 DOM 操作无关且耗时的任务放到 Web Workers(主线程之外的一个线程,无法通过 JavaScript 来访问 DOM)中去执行。
-
避免强制同步布局(JavaScript 强制将计算样式和布局操作提前到当前的任务中),可以调整策略,在修改 DOM 之前查询相关值。
-
避免布局抖动(指在一次 JavaScript 执行过程中,多次执行强制布局和抖动操作)
-
合理利用 CSS 合成动画
合成动画是直接在合成线程上执行的,这和在主线程上执行的布局、绘制等操作不同,如果主线程被 JavaScript 或者一些布局任务占用,CSS 动画依然能继续执行。 -
避免频繁的垃圾回收
如果在一些函数中频繁创建临时对象,那么垃圾回收器也会频繁地去执行垃圾回收策略。这样当垃圾回收操作发生时,就会占用主线程,从而影响到其他任务的执行,严重的话还会让用户产生掉帧、不流畅的感觉。
所以要尽可能优化储存结构,尽可能避免小颗粒对象的产生。
-
虚拟DOM:虚拟DOM和实际的DOM有何不同?
-
DOM 的缺陷
比如,我们可以调用document.body.appendChild(node)往 body 节点上添加一个元素,调用该 API 之后会引发一系列的连锁反应。首先渲染引擎会将 node 节点添加到 body 节点之上,然后触发样式计算、布局、绘制、栅格化、合成等任务,我们把这一过程称为重排。除了重排之外,还有可能引起重绘或者合成操作,形象地理解就是“牵一发而动全身”。另外,对于 DOM 的不当操作还有可能引发强制同步布局和布局抖动的问题,这些操作都会大大降低渲染效率。 -
什么是虚拟 DOM
将页面改变的内容应用到虚拟 DOM 上,仅仅是调整虚拟 DOM 的内部状态,在虚拟 DOM 收集到足够的改变时,再把这些变化一次性应用到真实的 DOM 上。
基于 React 和 Redux 构建 MVC 模型
渐进式网页应用(PWA):它究竟解决了Web应用的哪些问
题?
PWA,全称是 Progressive Web App(渐进式网页应用)。所以 PWA 所支持的首先是一个 Web 页面。
它是一套理念,渐进式增强 Web 的优势,并通过技术手段渐进式缩短和本地应用或者小程序的距离。基于这套理念之下的技术都可以归类到 PWA。
-
Web 应用 VS 本地应用
Web 应用缺少离线使用能力和消息推送的能力,Web 应用还缺少一级入口,也就是将 Web 应用安装到桌面,在需要的时候直接从桌面打开 Web 应用,而不是每次都需要通过浏览器来打开。
针对以上 Web 缺陷,PWA 提出了两种解决方案:通过引入 Service Worker 来试着解决离线存储和消息推送的问题,通过引入 manifest.json 来解决一级入口的问题。 -
什么是 Service Worker
主要思想是在页面和网络之间增加一个拦截器,用来缓存和拦截请求。 -
Service Worker 的设计思路
- 架构:让其运行在主线程之外
- 消息推送
- 安全:采用 HTTPS 协议
-
manifest.json 配置文件
PWA 还提供了 manifest.json 配置文件,可以让开发者自定义桌面的图标、显示名称、启动方式等信息,还可以设置启动画面、页面主题颜色等信息。
WebComponent:像搭积木一样构建Web应用
-
什么是组件化呢?
对内高内聚,对外低耦合:对内各个元素彼此紧密结合、相互依赖,对外和其他组件的联系最少且接口简单。 -
阻碍前端组件化的因素
- CSS 的全局属性会阻碍组件化
- DOM 也是阻碍组件化的一个因素,因为在页面中只有一个 DOM,任何地方都可以直接读取和修改 DOM
-
WebComponent 组件化开发
WebComponent 是一套技术的组合,提供了对局部视图封装的能力。具体涉及到了Custom elements(自定义元素)、Shadow DOM(影子 DOM)和 HTML templates(HTML模板)。- 使用 template 属性来创建模板
- 创建一个 GeekBang 的类
- 查找模板内容;
- 创建影子 DOM(将模板中的内容与全局 DOM 和 CSS 进行隔离,这样我们就可以实现元素和样式的私有化了);
- 再将模板添加到影子 DOM 上;
- 使用 customElements.define 来自定义元素。
- 像正常使用 HTML 元素一样使用该元素
-
浏览器如何实现影子 DOM
- 影子 DOM 中的元素对于整个网页是不可见的;
- 影子 DOM 的 CSS 不会影响到整个网页的 CSSOM,影子 DOM 内部的 CSS 只对内部的元素起作用。









网友评论