33-编译模块概述-element类型

79 阅读1分钟

测试用例

 describe("Element", () => {
    test("simple element", () => {
      const elementStr = "<div></div>";
      const ast = baseParse(elementStr);
      expect(ast.children[0]).toStrictEqual({
        type: NodeType.ELEMENT,
        tag: "div",
      });
    });
  });

实现

新增ELEMENT类型

ast.ts

export const enum NodeType {
  INTERPOLATION,
  SIMPLE_EXPRESSION,
+  ELEMENT,
}

新增ELEMENT类型判断

parse.ts

function parseChildren(context: { source: string }): any {
  const nodes: any = [];
  let node;
  const s = context.source;
  /** 判断字符串类型
   * 1. 为插值
   * 2. 为element
   */
  if (s.startsWith("{{")) {
    // {{ 开头,即认为是插值
    node = parseInterpolation(context);
  } else if (s.startsWith("<") && /[a-z]/i.test(s[1])) {
    // <开头,并且第二位是a-z,即认为是element类型
    node = parseElement(context);
  }
  nodes.push(node);
  return [node];
}

// 解析element
function parseElement(context: { source: string }): any {
  return {
    tag: "div",
    type: NodeType.ELEMENT,
  };
}

这里我们就简单完成了element类型判断

实现ELEMENT判断

// 解析element
function parseElement(context: { source: string }): any {
  // i 忽略大小写, ([a-z]*) 作为一个分组
+  const match = /^<([a-z]*)/i.exec(context.source);
  // 其中 tag[1] 就是匹配出来的 div,取反是为了避免为null报错,也可以写成 match && match[1]
+  const tag = match![1];
  /** 往后推进
   * 1. match[0]匹配出 <div
   * 2. match[0].length + 1,即下标<div>后开始
   */
+  advanceBy(context, match![0].length + 1);
  return {
    tag,
    type: NodeType.ELEMENT,
  };
}

处理闭合标签

前面实现了标签识别,并往后推进,我们还需识别闭合标签,并往后推进

// 增加枚举
const enum TagType {
  START,
  END,
}

// 解析element
function parseElement(context: { source: string }): any {
  // 这里调用两次 parseTag 处理前后标签
  const element = parseTag(context, TagType.START);
  // 处理闭合标签
  parseTag(context, TagType.END);
  return element;
}

function parseTag(context: { source: string }, type: TagType) {
  // i 忽略大小写, ([a-z]*) 作为一个分组,匹配<开头或者</开头的内容
  const match = /^<\/?([a-z]*)/i.exec(context.source);
  // 其中 tag[1] 就是匹配出来的 div,取反是为了避免为null报错
  const tag = match![1];
  /** 往后推进
   * 1. match[0]匹配出 <div
   * 2. match[0].length + 1,即下标<div>后开始
   */
  advanceBy(context, match![0].length + 1);
  // 处理闭合标签,就不需要return了
  if (type === TagType.END) return;
  return {
    tag,
    type: NodeType.ELEMENT,
  };
}