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

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

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

  • token 是怎么被划分的
<!-- 代码注释 -->
<p class="myP">xhzy</p>

这个标签会被拆分成:

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

详细步骤:
    ➀ 浏览器要从 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 树算法的流程为:

  • 当前节点 push 进栈顶;
  • 遇到属性,就添加到当前节点;
  • 遇到文本节点,如果当前节点是文本节点,则跟文本节点合并,否则入栈成为当前节点的子节点;
  • 遇到注释节点,作为当前节点的子节点;
  • 遇到 tag start 就入栈一个节点,当前节点就是这个节点的父节点;
  • 遇到 tag end 就出栈一个节点(还可以检查是否匹配)。

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值