企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[TOC] # HTML ## HTML文法定义(The HTML grammar definition) W3C组织制定规范定义了HTML的词汇表和语法。 <br> <br> ## 非上下文无关文法(Not a context free grammar) 正如在解析简介中提到的,上下文无关文法的语法可以用类似BNF的格式来定义。 <br> 不幸的是,所有的传统解析方式都不适用于html(当然我提出它们并不只是因为好玩,它们将用来解析css和js),html不能简单的用解析所需的上下文无关文法来定义。 <br>   Html有一个正式的格式定义——DTD(Document Type Definition文档类型定义)——但它并不是上下文无关文法,html更接近于xml,现在有很多可用的xml解析器,html有个xml的变体——xhtml,它们间的不同在于,html更宽容,它允许忽略一些特定标签,有时可以省略开始或结束标签。总的来说,它是一种soft语法,不像xml呆板、固执。 <br>   显然,这个看起来很小的差异却带来了很大的不同。一方面,这是html流行的原因——它的宽容使web开发人员的工作更加轻松,但另一方面,这也使很难去写一个格式化的文法。所以,html的解析并不简单,它既不能用传统的解析器解析,也不能用xml解析器解析。 <br> <br> ## HTML DTD Html适用DTD格式进行定义,这一格式是用于定义SGML家族的语言,包括了对所有允许元素及它们的属性和层次关系的定义。正如前面提到的,html DTD并没有生成一种上下文无关文法。 <br> DTD有一些变种,标准模式只遵守规范,而其他模式则包含了对浏览器过去所使用标签的支持,这么做是为了兼容以前内容。最新的标准DTD在[链接](http://www.w3.org/TR/html4/strict.dtd ) <br> <br> # DOM树 输出的树,也就是解析树,是由DOM元素及属性节点组成的。DOM是文档对象模型的缩写,它是html文档的对象表示,作为html元素的外部接口供js等调用。 树的根是“document”对象。 DOM和标签基本是一一对应的关系,例如,如下的标签: ~~~ <html> <body> <p> Hello DOM </p> <div><img src=”example.png” /></div> </body> </html> ~~~ 将会被转换为下面的DOM树: ![](https://box.kancloud.cn/dc9dbc56a001f62b528c5a888962d975_400x219.png) <br> 和html一样,DOM的规范也是由W3C组织制定的。访问[http://www.w3.org/DOM/DOMTR](http://www.w3.org/DOM/DOMTR),这是使用文档的一般规范。一个模型描述一种特定的html元素,可以在[http://www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109/idl-definitions.htm](http://www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109/idl-definitions.htm)查看html定义。 <br> <br> # 解析算法(The parsing algorithm) 正如前面章节中讨论的,hmtl不能被一般的自顶向下或自底向上的解析器所解析。原因是: * 这门语言本身的宽容特性 * 浏览器对一些常见的非法html有容错机制 * 解析过程是往复的,通常源码不会在解析过程中发生改变,但在html中,脚本标签包含的“document.write”可能添加标签,这说明在解析过程中实际上修改了输入。 <br> 不能使用正则解析技术,浏览器为html定制了专属的解析器。 Html5规范中描述了这个解析算法,算法包括两个阶段——符号化及构建树。 符号化是词法分析的过程,将输入解析为符号,html的符号包括开始标签、结束标签、属性名及属性值。 符号识别器识别出符号后,将其传递给树构建器,并读取下一个字符,以识别下一个符号,这样直到处理完所有输入。 ![](https://box.kancloud.cn/b4dc4e548d9012c25d5a5f0b4e5f17e5_308x400.png) <br> <br> # 符号识别算法(The tokenization algorithm) ## 词(token)是如何被拆分的 * 拆分:最小有意义单元 - token(词) * 词的种类大约只有标签开始、属性、标签结束、注释、CDATA 节点... * eg: ~~~ <p class="a">text text text</p> ~~~ * <p“标签开始”的开始; * class=“a” 属性 * `>` “标签开始”的结束; * text text text 文本; * /p>标签结束 ![](https://box.kancloud.cn/095422978cadcc6de9064bd05b1d927a_624x252.png) <br> # 树的构建算法 ## 状态机 ![](https://box.kancloud.cn/2d5744f3ba248f43604ab759907ab48e_768x739.png) * 为什么使用:我们每读入一个字符,其实都要做一次决策,而且这些决定是跟“当前状态”有关的 * 定义:把每个词的“特征字符”逐个拆开成独立状态,然后再把所有词的特征字符链合并起来,形成一个联通图结构。 * 绝大多数语言的词法部分都是用状态机实现的,HTML 官方文档规定了 80 个状态 * 最终把字符流拆分成词 <br> ## 构建 DOM 树 通过栈来实现,当接收完所有输入,栈顶就是最后的根节点,我们 DOM 树的产出,就是这个 stack 的第一项。 <br> 实现一个Node 类,所有的节点都会是这个 Node 类的实例。不一样的 HTML 节点对应了不同的 Node 的子类,此处的实现,我们进行简化,只把 Node 分为 Element 和 Text ~~~ function Element(){ this.childNodes = []; } function Text(value){ this.value = value || ""; } ~~~ * 规则: * token中`tag start`和`tag end`需要成对实现,使用的栈正是用于匹配开始和结束标签的方案(编译原理技巧) * Text 节点:把相邻的 Text 节点合并起来,当词(token)入栈时,检查栈顶是否是 Text 节点,果是的话就合并 Text 节点 * 构建过程: (默认:源代码完全遵循 xhtml,HTML 具有很强的容错能力,奥妙在于当 tag end 跟栈顶的 start tag 不匹配的时候如何处理,暂时不考虑) * 栈顶元素就是当前节点; * 遇到属性,就添加到当前节点; * 遇到文本节点,如果当前节点是文本节点,则跟文本节点合并,否则入栈成为当前节点的子节点; * 遇到注释节点,作为当前节点的子节点; * 遇到 tag start 就入栈一个节点,当前节点就是这个节点的父节点 * 遇到 tag end 就出栈一个节点(还可以检查是否匹配) <br> ## 浏览器容错(Browsers error tolerance) 你从来不会在一个html页面上看到“无效语法”这样的错误,浏览器修复了无效内容并继续工作。 <br> 以下面这段html为例: ~~~ <html> <mytag> </mytag> <div> <p> </div> Really lousy HTML </p> </html> ~~~ <br> 这段html违反了很多规则(mytag不是合法的标签,p及div错误的嵌套等等),但是浏览器仍然可以没有任何怨言的继续显示,它在解析的过程中修复了html出现的错误。 <br> 浏览器都具有错误处理的能力,但是,另人惊讶的是,这并不是html最新规范的内容,就像书签及前进后退按钮一样,它只是浏览器长期发展的结果。一些比较知名的非法html结构,在许多站点中出现过,浏览器都试着以一种和其他浏览器一致的方式去修复。 <br>   Html5规范定义了这方面的需求,webkit在html解析类开始部分的注释中做了很好的总结。 <br>   解析器将符号化的输入解析为文档并创建文档,但不幸的是,我们必须处理很多没有很好格式化的html文档,至少要小心下面几种错误情况。 <br>   1. 在未闭合的标签中添加明确禁止的元素。这种情况下,应该先将前一标签闭合   2. 不能直接添加元素。有些人在写文档的时候会忘了中间一些标签(或者中间标签是可选的),比如HTML HEAD BODY TR TD LI等   3. 想在一个行内元素中添加块状元素。关闭所有的行内元素,直到下一个更高的块状元素   4. 如果这些都不行,就闭合当前标签直到可以添加该元素。 <br> ## 解析结束时的处理(Action when the parsing is finished)  在这个阶段,浏览器将文档标记为可交互的,并开始解析处于延时模式中的脚本——这些脚本在文档解析后执行。  文档状态将被设置为完成,同时触发一个load事件。  Html5规范中有符号化及构建树的完整算法(http://www.w3.org/TR/html5/syntax.html#html-parser)。 <br> <br> # 参考资料 [浏览器工作原理(四):HTML解析器 HTML Parser](https://blog.csdn.net/lxcao/article/details/52860746) 重学前端