前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Webkit底层原理(3)--HTML解释器

Webkit底层原理(3)--HTML解释器

作者头像
从入门到进错门
发布2019-10-22 14:45:27
8220
发布2019-10-22 14:45:27
举报
文章被收录于专栏:前端菜鸟变老鸟

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/caomage/article/details/102072801

1. 解释过程

HTML解释器的工作就是将从网络或本地磁盘获取的HTML网页和资源从字节流解释成DOM树结构。如下图:

图中将这一过程描述得很清晰:首先是字节流,经过解码之后是字符流,然后通过词法分析器会被解释成词语(Tokens),之后经过语法分析器构建成节点,最后这些节点被组成一颗DOM树。

2. 词法分析

在进行词法分析之前,解释器首先要做的就是检查该网页内容使用的编码格式,以便后面使用合适的解码器。如果解释器在HTML网页中找到了设置的编码格式,Webkit会使用相应的解码器将字节流转换成特定格式的字符串。如果没有特殊的格式,词法分析器HTMLTokenizer可以直接进行词法分析。 词法分析的工作都是由HTMLTokenier来完成,简单来说,它就是一个状态机–输入的是字符串,输出的是一个个的词语。因为字节流可能是分段的,所以输入的字符串可能也是分段的,但是这对词法分析器来说没什么特别之处,它会自己维护内部的状态信息。 词法分析器的主要接口是nextToken函数,调用者只需要将字符串传入,然后就会得到一个词语,并对传入的字符串设置相应的信息,表示当前处理完的位置,如此循环。如果词法分析器遇到错误,则报告状态错误码。 而对于词语的类别,Webkit是定义了很少,HTMLToken定义了6种词语类别,包括:DOCTYPE、StartTag、EndTag、Comment、Character和EndOfFile。这里不涉及HTML的标签类型等信息,那是后面语法分析的工作。

3. XSSAuditor验证词语

XSSAuditor是一个重要的类,是面试中会经常遇到的一个问题。 当词语生成之后,Webkit需要使用XSSAuditor来验证词语流(Token Stream)。XSS指的是Cross Site Security,主要是针对安全方面的考虑。 根据XSS的安全机制,对于解析出来的这些词语,可能会阻碍某些内容的进一步执行,所以XSSAuditor主要负责过滤这些被阻止的内容,只有通过的词语词啊会作后面的处理。详细的内容后面的文章我会再做介绍。

4. 词语到节点,从节点到DOM树

经过词法分析器解释之后的词语随之被XSSAuditor过滤并且在没有被阻止之后,将被Webkit用来构建DOM节点。主要是利用之前分成的6种词语,生成对应的节点。 因为HTML文档的Tag标签是有开始和结束标记的,所以构建这一过程可以使用栈结构来帮忙。其中,使用一个栈来保存元素节点,其中的元素节点是当前有开始标记但是还没有结束标记的元素节点。想象一下HTML文档的特点,例如:

代码语言:javascript
复制
<body>
  <div>
  	<span></span>
  </div>
</body>

当解释到span标签元素的开始标记时,栈中的元素就是body、div和span,当遇到span的结束标记时,span出栈,span时div的子女;当遇到div的结束标记时,div出栈,表明div和它的子女都已经处理完毕,以此类推。

5. 线程化的解释器

顾名思义,线程化的解释器就是利用单独的线程来解释HTML文档。因为在Webkit中,网络资源的字节流自IO线程传递给渲染线程之后,后面的解释、布局和渲染等工作基本上都是工作在该线程,也就是渲染线程完成的。因为DOM树只能在渲染线程上创建和访问,这就是说构建DOM树的过程只能在渲染线程中进行。但是,从字符串到词语这个阶段可交给单独的线程来做,Chromium就是这个思想。

6. JavaScript的执行

在HTML解释器工作过程中,可能会有JavaScript代码需要执行,它发生在将字符串解释成词语之后、创建各种节点的时候。这也是为什么全局执行的JavaScript代码不能访问DOM树的原因–因为DOM树还没有被创建完成。 Webkit将DOM树创建过程中需要执行的JavaScript代码交给HTMLScriptRunner类来执行。工作方式很简单,就是利用JavaScript引擎来执行Node节点中包含的代码。 因为JavaScript代码可能会调用例如document。write()来修改文档结构,所以JavaScript代码的执行会阻塞后面节点的创建,同时当然也会阻塞后面资源的下载,这时候Webkit对需要什么资源一无所知,这导致了资源不能够并发的下载这种性能问题。所以使用JavaScript的时候有以下两点优化建议:

  1. script标签加上async属性,表明这是一个可以异步执行的代码;
  2. script标签放到body元素的最后,也就是</body>之前。

此外,针对资源不能够并发的下载这种性能问题,Webkit使用预扫描和预加载机制来解决。具体的做法是:当遇到需要执行JavaScript代码的时候,Webkit先暂停当前JavaScript代码的执行,使用预扫描器来扫描后面的词语,如果发现需要使用其他资源,就会使用预加载器发送请求获取资源,在这之后,才开始执行JavaScript代码。预扫描器本身并不难创建节点,也不会创建DOM树,所以速度比较快。就是如此,还是建议使用上面的两点优化建议。

当DOM树构建完之后,Webkit触发DOMContentLoaded事件,注册在该事件上的JavaScript函数会被调用。当所有资源都被加载完之后,Webkit会触发onload事件。

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 解释过程
  • 2. 词法分析
  • 3. XSSAuditor验证词语
  • 4. 词语到节点,从节点到DOM树
  • 5. 线程化的解释器
  • 6. JavaScript的执行
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档