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