6月阅读周·WebKit技术内幕 | HTML解释器和DOM模型
背景
去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。
没有计划的阅读,收效甚微。
新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。
这个“玩法”虽然常见且板正,但是有效,已经坚持阅读五个月。
已读完书籍:《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScript(中卷)》、《你不知道的JavaScript(下卷)》、《数据结构与算法JavaScript描述》。
当前阅读周书籍:《WebKit技术内幕》。
HTML解释器和DOM模型
DOM模型
DOM标准
DOM(Document Object Model)的全称是文档对象模型,它可以以一种独立于平台和语言的方式访问和修改一个文档的内容和结构。这里的文档可以是HTML文档、XML文档或者XHTML文档。DOM以面向对象的方式来描述文档,在HTML文档中,Web开发者可以使用JavaScript语言来访问、创建、删除或者修改DOM结构,其主要目的是动态改变HTML文档的结构。
DOM定义的是一组与平台、语言无关的接口,该接口允许编程语言动态访问和更改结构化文档。使用DOM表示的文档被描述成一个树形结构,使用DOM的接口可以对DOM树结构进行操作。W3C标准化组织定义一系列DOM接口。
DOM树
DOM结构构成的基本要素是“节点”,而文档的DOM结构就是由层次化的节点组成。在DOM模型中,节点的概念很宽泛,整个文档(Document)就是一个节点,称为文档节点。HTML中的标记(Tag)也是一种节点,称为元素(Element)节点。还有一些其他类型的节点,例如属性节点(标记的属性)、Entity节点、ProcessingIntruction节点、CDataSection节点、注释(Comment)节点等。
HTML解释器
解释过程
HTML解释器的工作就是将网络或者本地磁盘获取的HTML网页和资源从字节流解释成DOM树结构。
WebKit中这一过程是:首先是字节流,经过解码之后是字符流,然后通过词法分析器会被解释成词语(Tokens),之后经过语法分析器构建成节点,最后这些节点被组建成一棵DOM树。
词法分析
词法分析的工作都是由HTMLTokenizer类来完成,简单来说,它就是一个状态机——输入的是字符串,输出的是一个个的词语。因为字节流可能是分段的,所以输入的字符串可能也是分段的,但是这对词法分析器来说没有什么特别之处,它会自己维护内部的状态信息。
词法分析的工作都是由HTMLTokenizer类来完成,简单来说,它就是一个状态机——输入的是字符串,输出的是一个个的词语。因为字节流可能是分段的,所以输入的字符串可能也是分段的,但是这对词法分析器来说没有什么特别之处,它会自己维护内部的状态信息。
词法分析器的主要接口是“nextToken”函数,调用者只需要将字符串传入,然后就会得到一个词语,并对传入的字符串设置相应的信息,表示当前处理完的位置,如此循环。如果词法分析器遇到错误,则报告状态错误码。
DOM的事件机制
事件的工作过程
事件在工作过程中使用两个主体,第一个是事件(event),第二个是事件目标(EventTarget)。
每个事件都有属性来标记该事件的事件目标。当事件到达事件目标(如一个元素节点)的时候,在这个目标上注册的监听者(Event Listeners)都会被触发调用,当然这些监听者的调用顺序是不固定的,所以不能依赖监听者注册的顺序来决定你的代码逻辑。
WebKit的事件处理机制
DOM的事件分为很多种,与用户相关的只是其中的一种,称为UIEvent,其他的包括CustomEvent、MutationEvent等。UIEvent又可以分为很多种,包括但是不限于FocusEvent、MouseEvent、KeyboardEvent、CompositionEvent等。
基于WebKit的浏览器事件处理过程,首先是做HitTest,查找事件发生处的元素,检测该元素有无监听者。如果网页的相关节点注册了事件的监听者,那么浏览器会把事件派发给WebKit内核来处理。同时,浏览器也可能需要理解和处理这样的事件。这主要是因为,有些事件浏览器必须响应从而对网页作默认处理。
影子(Shadow)DOM
什么是影子DOM
当网页的开发者需要访问网页DOM树的时候,这些控件内部的DOM子树都会暴露出来,这些暴露的节点不仅可能给DOM树的遍历带来很多麻烦,而且也可能给CSS的样式选择带来问题,因为选择器无意中可能会改变这些内部节点的样式,从而导致很奇怪的控件界面。
当使用JavaScript代码访问HTML文档的DOM树的时候,通常的接口是不能直接访问到影子DOM子树中的节点的,JavaScript代码只能通过特殊的接口方式。
因为影子DOM的子树在整个网页的DOM树中不可见,那么事件是如何处理的呢?事件中需要包含事件目标,这个目标当然不能是不可见的DOM节点,所以事件目标其实就是包含影子DOM子树的节点对象。事件捕获的逻辑没有发生变化,在影子DOM子树内也会继续传递。当影子DOM子树中的事件向上冒泡的时候,WebKit会同时向整个文档的DOM上传递该事件,以避免一些很奇怪的行为。
WebKit的支持
WebKit已经支持影子DOM的规范草案,虽然还存在一些问题。支持影子DOM的相关类在目录“Source/core/dom/shadow”下,里面的主要类是ShadowRoot,表示的是影子DOM的根节点。ShadowRoot类继承自DocumentFragment类,所以它同样有Node节点的属性和方法,因而在影子DOM树内部,遍历树没有什么特别不同的地方。
总结
在WebKit中,资源最初的表示就是字节流,这些字节流可以是网络传输来的,也可以是本地文件,那么字节流在接下来需要经过怎样的处理呢?处理后变成了什么呢?本篇主要分享在研究W3C的DOM模型之后,深入WebKit的核心部分,剖析WebKit的HTML解释器是如何将从网络或者本地文件获取的字节流转成内部表示的结构——DOM树。
作者介绍
非职业「传道授业解惑」的开发者叶一一。
《趣学前端》、《CSS畅想》等系列作者。华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏⭐️ | 留言📝。
- 点赞
- 收藏
- 关注作者
评论(0)