测试用例
/*
* @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,
},
};
}