了解浏览器接受收 html文件 并渲染的全过程

有限状态机处理字符

https://www.cnblogs.com/ssaylo/p/13130138.html ,先看这篇文章,了解 HTTP 请求是如何发送与接收的。

前言——如何在不适用正则与 JavaScript Api 接口的条件下查找字符数按所在的位置?

winter 前端进阶训练营第六周

有限状态机

  • 每一个状态都是一个机器

  • 每一个机器知道下一个状态

    • moore(每个机器都有确定的下一个状态)
    • mealy(根据输入决定下一个状态)(mealy 是个人名,不用去管他)

使用有限状态机处理字符串

  • v1.0

    // 不用 api,不用正则,写一个 match
    function match(string) {
      for (let c of string) {
        if (c === 'a') {
          return true;
        }
      }
      return false;
    }
    
    match('I m groota');
    
    
  • v2.0

    // 这里会有个问题, "a" 与 "b" 不相邻的时候也会判断为 true,怎么办呢???
    function match(string) {
      let foundA = false;
      for (let c of string) {
        if (c === 'a') {
          foundA = true;
        } else if (foundA && c == 'b') {
          return true;
        }
      }
      return false;
    }
    console.log(match('I acbm groot'));
    
    // 很简单
    function match2(string) {
      let foundA = false;
      for (let c of string) {
        if (c === 'a') {
          foundA = true;
        } else if (foundA && c === 'b') {
          return true;
        } else {
          //////////////////////在这里多加一句话//////////////////////
          foundA = false;
        }
      }
      return false;
    }
    console.log(match2('I acbm groot'));
    
  • v3.0

    // 刚在写的是匹配 a,b 两个变量,那么如果要匹配三个变量呢?如果要匹配四个变量呢?在这里我们就使用到了有限状态机
    // 首先我们定义五个状态,初始值为 false
    // 然后在查找过程找,对状态进行变更
    // 字符串中存在字符,便变更对应的状态为 true
    // 倘若一旦有一个字符不匹配,重置所有的状态
    function match(string) {
      let foundA = false;
      let foundB = false;
      let foundC = false;
      let foundD = false;
      let foundE = false;
      for (let c of string) {
        if (c === 'a') foundA = true;
        else if (foundA && c === 'b') foundB = true;
        else if (foundB && c === 'c') foundC = true;
        else if (foundC && c === 'd') foundD = true;
        else if (foundD && c === 'e') foundE = true;
        else if (foundE && c === 'f') return true;
        else {
          foundA = false;
          foundB = false;
          foundC = false;
          foundD = false;
          foundE = false;
        }
      }
      return false;
    }
    
    console.log(match('I abm groot'));
    console.log(match('I abcdef'));
    
  • v4.0

    // 每个函数就是一个状态
    // 函数参数就是输入
    // 返回的是下一个状态
    function state(input) {
      return next;
    }
    
    function match(string) {
      let state = start;
      for (let c of string) {
        console.log(c);
        state = state(c);
      }
      return state === end;
    }
    
    function start(c) {
      if (c === 'a') return foundA;
      else return start;
    }
    
    function end(c) {
      return end;
    }
    
    function foundA(c) {
      if (c === 'b') return foundB;
      else return start(c);
    }
    
    function foundB(c) {
      if (c === 'c') return end;
      else return start(c);
    }
    
    function foundC(c) {
      if (c === 'd') return foundD;
      else return start(c);
    }
    
    function foundD(c) {
      if (c === 'e') return foundE;
      else return start(c);
    }
    
    function foundE(c) {
      if (c === 'f') return end;
      else return start(c);
    }
    
    console.log(match('aacbc'));
    console.log(match('aacbcdefg'));
    
  • v5.0

    function match(string) {
      let state = start;
      for (let c of string) {
        state = state(c);
      }
      return state === end;
    }
    
    function start(c) {
      if (c === 'a') return foundA;
      else return start;
    }
    
    function end(c) {
      return end;
    }
    
    function foundA(c) {
      if (c === 'b') return foundB;
      else return start;
    }
    
    function foundB(c) {
      if (c === 'c') return foundC;
      else return start;
    }
    
    function foundC(c) {
      if (c === 'a') return foundA2;
      else return start;
    }
    
    function foundA2(c) {
      if (c === 'b') return foundB2;
      else return start;
    }
    
    function foundB2(c) {
      if (c === 'x') return end;
      else if (c === 'c') return foundC;
      else return start;
    }
    
    console.log(match('abcabcabx'));
    
  • 匹配“abababx”

    • 可选: 撞击处理完全未知的pattern

      • 参考: 字符串KMP算法

        (这里先留个坑位了,算法刷完回来解决)

        参考链接

HTML parser

第一步: 拆分文件

  • 为了方便文件管理,把parse单独拆到文件中
  • parser接受HTML文本作为参数,返回一颗DOM树
  • 见parse-1.js

第二步: 创建状态机

  • 使用FSM实现HTML分析
  • 在HTML标签中,已经规定了HTML的状态
  • 见parse-2.js

第三步: 解析标签

  • 主要的标签有:开始标签,结束标签和自封闭标签
  • 见parse-3.js

第四步: 创建元素

  • 在状态机中,除了状态迁移,我们还要加入业务逻辑
  • 在标签结束状态提交标签token
  • 见parse-4.js

第五步: 处理属性

  • 属性值分为单引号、双引号、无引号三种写法,因此需要较多状态处理
  • 处理属性的方式和标签类似
  • 属性结束时,把属性加到标签token上
  • 见parse-5.js

第六步: 构建DOM树

  • 从标签构建DOM树的基本技巧是使用栈
  • 遇到开始标签时创建元素并入栈,遇到结束标签的时候出栈
  • 自封闭标签可以视为入栈后立刻出栈
  • 任何元素的父元素是它入栈前的栈顶
  • 见parse-6.js

第七步: 处理文本节点

  • 文本节点与自封闭标签处理类似
  • 多个文本节点需要合并
  • 见parse-7.js

CSS Computing

第一步: 收集CSS规则

  • 遇到 style 标签时,我们把 CSS 规则保存起来
  • 我们调用 CSS Parser 来分析 CSS 规则
  • 我们必须要仔细研究此库分析 CSS 规则的格式
  • 见computedCSS-1.js

第二步: 添加调用

  • 当我们创建一个元素后,立即计算 CSS
  • 理论上,当我们分析一个元素时,所有 CSS 规则已经收集完毕
  • 在真实浏览器中,可能遇到写在 body 的 style 标签,需要重新 CSS 计算的情况,这里我们忽略
  • 见computedCSS-2.js

第三步: 获取父元素序列

  • 在 computeCss 函数中,我们必须知道元素的所有父元素才能判断元素与规则是否匹配
  • 我们从上一步骤的 stack,可以获取本元素所有的父元素
  • 因为我们首先获取的是”当前元素“,所以我们获得和计算父元素匹配的顺序是从内向外
  • 见computedCSS-3.js

第四步: 拆分选择器

  • 选择器也要从当前元素从外排列
  • 复杂选择器拆成针对单个元素的选择器,用循环匹配父元素队列
  • 见computedCSS-4.js

第五步: 计算选择器与元素匹配关系

  • 根据选择器的类型和元素属性,计算是否与当前元素匹配
  • 这里仅实现了三种基本选择器,实际的浏览器中要处理复合选择器
  • 见computedCSS-5.js

第六步: 生成Computed属性

  • 一旦选择匹配,就应用选择器到元素上,形成 computedStyle
  • 见computedCSS-6.js

第七步: 确定覆盖关系

  • CSS 规则根据 specificity 和后来优先规则覆盖
  • specificity 是个四元组,越左边权重越高
  • 一个 CSS 规则的 specificity 根据包含的简单选择器相加而成
  • 见computedCSS-7.js
posted @ 2020-06-16 10:51  林来  阅读(530)  评论(0编辑  收藏  举报