从零开始,编写一个HTML模版引擎(二)
一、概览从今天开始,让我们从头来写一个带diff的前端模版引擎,该功能会分成几期做完。
首先我们看看这个系列的目标是什么,如下图:
今天我们主要实现基础的模版解析。
二、场景分析
首先,假设一下使用场景:
我们需要一个parser()
方法。将模版字符串传入。parser()
方法在解析html标签的各个阶段调用我们的回调函数,方便我们生成预期的结构。
下面用代码演示一下使用方法:
import { parser } from '@jax/html-parser'
// api参照 https://www.npmjs.com/package/htmlparser2
parser('Jax', {
onStart(tag: string, attr: string) {},
onText(text) {},
onEnd(tag) {}
})
ok,知道怎么用了之后,我们就来实现parser()
方法吧。
解析html
模版的方式有很多,此处我们采用正则匹配的方法,下面让我们图来简单说明下编码逻辑:
确定了代码逻辑,接下来我们准备需要的几个正则表达式
// 匹配起始标签和属性
const startReg = /^<(\w+)\s?(.*?)\/?>/
// 匹配结束标签属性
const endReg = /^<\/(\w+)>/
// 匹配标签内容
const textReg = /^>?(.*?)</
// 空字符
const blockReg = /^>?\s+\S/
正则表达式准备好后,接下来实现主方法
3.3 实现parser()
export const parser = (html: string, options: optionsType) => {
// 获取传入的几个回调函数
const { onStartTag, onText, onEndTag } = options
html = html.trim()
let matched = null
// 扫描文本,默认我们认为模版字符串的最后一个字符一定是>,所以此处>1即可
while (html.length > 1) {
// 判断是否为结束标签
if (html.startsWith('')) {
matched = matchByReg(endReg)
onEndTag && onEndTag(matched[1]) // 执行回调
} else if (html.startsWith('<')) {
// 开始标签
matched = matchByReg(startReg)
onStartTag && onStartTag(matched[1], attrParser(matched[2])) // 执行回调
// 特殊处理下自闭合标签
if (matched[0].endsWith('/>')) {
onEndTag && onEndTag(matched[1]) // 执行回调
}
} else if (html.startsWith('>')) {
// 去掉可能存在的空格
matchByReg(blockReg, false)
if (!html.startsWith('<')) {
// 获取标签中的内容
matched = matchByReg(textReg, false)
if (matched[1]) {
onText && onText(matched[1]) // 执行回调
}
}
}
}
}
代码中用于匹配和截取的 *** 作我们给提成matchByReg()
方法。注意,此方法需要 *** 作html
模版字符串,所以该方法是声明在parser()
方法中
const matchByReg = (
reg: RegExp,
isThrow: boolean = true
): RegExpMatchArray => {
let matched = html.match(reg)
if (matched === null) {
if (!isThrow) {
return []
}
return throwError()
}
// 匹配到了后处理字符串
let len = matched.index! + matched[0].length
html = html.substring(len - 1)
return matched
}
此时像我们在场景分析中那样使用,parser()
方法已经能够正常工作了,下一期我们来处理目标结构生成
。
循序渐进,不忘初心,我们明天见
有问题请留言
或发邮件: liujax@126.com
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)