32-编译模块概述-插值类型

85 阅读2分钟

测试用例

/*
 * @Author: Lin ZeFan
 * @Date: 2022-04-10 10:45:42
 * @LastEditTime: 2022-04-10 10:59:45
 * @LastEditors: Lin ZeFan
 * @Description: 
 * @FilePath: \mini-vue3\src\compiler-core\__tests__\parse.spec.ts
 * 
 */
 
 import { baseParse } from "../src/parse";
 
   describe("Interpolation", () => {
    test("simple interpolation", () => {
      // 1. 看看是不是一个 {{ 开头的
      // 2. 是的话,那么就作为 插值来处理
      // 3. 获取内部 message 的内容即可
      const ast = baseParse("{{message}}");
      const interpolation = ast.children[0];

      expect(interpolation).toStrictEqual({
        type: "interpolation",
        content: {
          type: "simple_expression",
          content: `message`,
        },
      });
    });
  });

实现

伪实现

先返回一个固定的内容,通过测试用例

/*
 * @Author: Lin ZeFan
 * @Date: 2022-04-10 10:45:42
 * @LastEditTime: 2022-04-10 10:58:35
 * @LastEditors: Lin ZeFan
 * @Description:
 * @FilePath: \mini-vue3\src\compiler-core\src\parse.ts
 *
 */

export function baseParse(content: string) {
  const context = createContext(content);
  return createRoot(parseChildren(context));
}

// 创建上下文
function createContext(content: string) {
  return {
    source: content,
  };
}

// 创建 ast 根节点
function createRoot(children) {
  return {
    children,
  };
}

// 创建 children
function parseChildren(context: { source: string }): any {
  const nodes: any = [];
  let node;
  // 如果字符串以 {{ 开头,则认为是解析插值表达式
  if (context.source.startsWith("{{")) {
    node = parseInterpolation(context);
  }
  nodes.push(node);
  return [node];
}

// 解析插值表达式
function parseInterpolation(context: { source: string }) {
  return {
    type: "interpolation",
    content: {
      type: "simple_expression",
      content: "message",
    },
  };
}

解析插值是在 parseInterpolation 里完成的,前面我们写了伪代码,现在来完善这部分代码。

function parseInterpolation(context: { source: string }) {
  // 插值开始字符
  const openDelimiter = "{{";
  // 插值结束字符
  const closeDelimiter = "}}";
  // 找到插值结束字符的位置
  const closeIndex = context.source.indexOf(
    closeDelimiter,
    openDelimiter.length
  );
  // 切割掉 {{
  advanceBy(context, openDelimiter.length);
  // 找到 }} 前的内容
  const rawContentLength = closeIndex - closeDelimiter.length;
  // 截取插值表达式里的内容
  const content = context.source.slice(0, rawContentLength);
  // 完成{{}}的内容匹配,切割掉匹配完成的内容,继续往前推进,解析后面的内容
  advanceBy(context, rawContentLength + closeDelimiter.length);
  return {
    type: "interpolation",
    content: {
      type: "simple_expression",
      content,
    },
  };
}

function advanceBy(context, length: number) {
  context.source = context.source.slice(length);
}

抽离 AST Node 类型

新建 AST Node

/*
 * @Author: Lin ZeFan
 * @Date: 2022-04-10 10:45:42
 * @LastEditTime: 2022-04-10 11:25:29
 * @LastEditors: Lin ZeFan
 * @Description:
 * @FilePath: \mini-vue3\src\compiler-core\src\ast.ts
 *
 */

export const enum NodeType {
  INTERPOLATION,
  SIMPLE_EXPRESSION,
}

修改类型匹配

import { NodeType } from "./ast";
function parseInterpolation(context: { source: string }) {
  // other code
  
  return {
    type: NodeType.INTERPOLATION,
    content: {
      type: NodeType.SIMPLE_EXPRESSION,
      content,
    },
  };
}

最后同步修改测试用例

import { NodeType } from "../src/ast";
import { baseParse } from "../src/parse";

 describe("Interpolation", () => {
    test("simple interpolation", () => {
      // 1. 看看是不是一个 {{ 开头的
      // 2. 是的话,那么就作为 插值来处理
      // 3. 获取内部 message 的内容即可
      const ast = baseParse("{{message}}");
      const interpolation = ast.children[0];

      expect(interpolation).toStrictEqual({
        type: NodeType.INTERPOLATION,
        content: {
          type: NodeType.SIMPLE_EXPRESSION,
          content: `message`,
        },
      });
    });
  });

处理边缘情况

现在处理的是 {{message}},那如果用户多输了几个空格 {{ message }},匹配就有问题了

给content多做一下 trim即可

function parseInterpolation(context: { source: string }) {
  // 插值开始字符
  const openDelimiter = "{{";
  // 插值结束字符
  const closeDelimiter = "}}";
  // 找到插值结束字符的位置
  const closeIndex = context.source.indexOf(
    closeDelimiter,
    openDelimiter.length
  );
  // 切割掉 {{
  advanceBy(context, openDelimiter.length);
  // 找到 }} 前的内容
  const rawContentLength = closeIndex - closeDelimiter.length;
  // 截取插值表达式里的内容
+  const content = (context.source.slice(0, rawContentLength)).trim();
  // 完成{{}}的内容匹配,切割掉匹配完成的内容,继续往前推进,解析后面的内容
  advanceBy(context, rawContentLength + closeDelimiter.length);
  return {
    type: NodeType.INTERPOLATION,
    content: {
      type: NodeType.SIMPLE_EXPRESSION,
      content,
    },
  };
}