认识浏览器第二步解析代码、构建DOM树

2020-11-23  本文已影响0人  Lee弟弟

浏览器是怎么解析代码的:

    HTML 的解构不算复杂,我们日常开发 90% 的“词”(指编译原理的术语 token,表示最小的有意义的单元),种类大约只有标签开始、属性、标签结束、注释、CDATA 节点几种。
麻烦主要麻烦在 HTML 跟 SGML 的千丝万缕的联系,解析代码的时候还要兼容 “<?” 和 “<%” ,报错了也不能影响页面的展现。

<!-- 代码注释 -->
<p class="myP">xhzy</p>

这个标签会被拆分成:

token 说明
< !-- 代码注释 --> 注释
<p <p 标签的开始
class="myP" 属性
> <p 标签的结束
xhzy 文本节点
</p> 结束标签

详细步骤:
    ➀ 浏览器要从 http 请求里拿到的字符流里读取字符,首先接收到 “<” ,浏览器就排除文本节点的可能。
    ➁ 再读一个字符为 p ,浏览器就会知道这不是注释和 CDATA,如果不是 p 便签是 span 等多字符组成的便签浏览器会一直读直到遇到空格或 “>”,就能完整的读到一个标签开始的 token 。
    ➂ 浏览器根据提前写好的规则读取出其他属性或文本节点的内容。

实际上,浏览器每读一个字符都要做一次决策,都和 ”当前状态“ 有关。这就要引入状态机的概念。可以参考官方文档,官方文档规定了80个状态机,根据字符的特性判断它的状态。

因为浏览器用状态机做词法分析,所以浏览器会把每个 token 的特征标识拆成独立的状态,然后再把所有词的特征标识链合并起来,形成一个连通图解构。

简单实现一个获取初识状态机的函数编写,假设我们把浏览器读到的第一个字符当作参数存进 beginState:

const beginState = content => {
  if (content === '&') { 
    return characterReference;
  }
  if (content === '<') { 
    return tagOpen;
  }
  else if (content === '>') {
    return tagEnd;    
  }
  else if (.....
}

执行完 beginState 函数会获取到初始状态机,后续字符会带着当前的状态机(beginState 返回的 state)做对应的词法分析抛出相应的 token ,一层层递进下去直到标签关闭,循环重复执行这个流程直到分析到字符流最后一个字符,就会把字符流拆成 一个个 token 了。

构建 DOM 树

要把上一个步骤获取到的 token 转换成 DOM 树,这个过程是通过栈来实现的。模拟构建 DOM 树的方法:

const buildDOMTree = () => {
  const stack = [];
  const transformInput = token => {
    /**
      * 构建DOM树的算法
      */
  }
  const getRoot = () => {
    return stack[0];
  }
}

上述的 transformInput 方法 token 入参是词法分析抛出的 token,可以放到词法分析抛出 token 的时候调用,边做词法分析边构造 DOM 树。

构造 dom 树算法的流程为:

上述规则都是在代码正确的情况下工作的,当代码不正确的时候浏览器还能很好的容错,这其中的规则相当复杂,不过 W3C 已经帮我们整理好了全部规则

END......我是个有底线的家伙......END

上一篇 下一篇

猜你喜欢

热点阅读