让我们一起来构建一个模板引擎(一)

假设我们要生成下面这样的 html 字符串:


要求姓名以及 中的内容是根据变量动态生成的,也就是这样的:

没接触过模板的同学可能会想到使用字符串格式化的方式来实现:

这种方案有一个很明显的问题那就是,需要拼接两个 html 片段。 使用过模板技术的同学应该很容易就想到,在 Web 开发中生成 HTML 的更常用的办法是使用模板:

本系列文章要讲的就是如何从零开始实现一个这样的模板引擎( Template )。

使用技术

我们将使用将模板编译为 python 代码的方式来解析和渲染模板。 比如上面的模板将被编译为如下 python 代码:

然后通过 exec 执行生成的代码,之后再执行 render_function() 就可以得到我们需要的 html 字符串了:

模板引擎的核心技术就是这些了,下面让我们一步一步的实现它吧。

CodeBuilder

我们都知道 python 代码是高度依赖缩进的,所以我们需要一个对象用来保存我们生成代码时的当前缩进情况, 同时也保存已经生成的代码行(可以直接在 github 上下载 template1a.py ):

forwardbackward 方法可以用来控制缩进前进或后退一步,比如在生成 if 语句的时候:

Template

这个模板引擎的核心部分就是一个 Template 类,用法:

一切魔法都在 Template 类里。下面我们写一个基本的 Template 类(可以直接在 github 上下载 template1b.py ):

以上就是 Template 类的核心方法了。我们之后要做的就是实现和完善 _parse_text 方法。 当模板字符串为空时生成的代码如下:

可以看到跟上面[使用技术]那节所说生成的代码是类似的。下面我们就一起来实现这个 _parse_text 方法。

变量

首先要实现是对变量的支持,模板语法是 {{ variable }} 。 既然要支持变量,首先要做的就是把变量从模板中找出来,这里我们可以使用正则表达式来实现:

知道了如何匹配变量语法,下面我们要把变量跟其他的模板字符串分割开来,这里还是用的 re:

这里的正则之所以加了个分组是因为我们同时还需要用到模板里的变量。 分割开来以后我们就可以对每一项进行解析了。支持 {{ variable }} 语法的 Template 类增加了如下代码 (可以直接在 github 上下载 template1c.py ):

_parse_text 中之所以要用 repr ,是因为此时需要把 token 当成一个普通的字符串来处理, 同时需要考虑 token 中包含 "' 的情况。 下面是几种有问题的写法:

  • 'str({})'.format(token): 这种是把 token 当成变量来用了,生成的代码为 str(token)
  • '"{}"'.format(token): 这种虽然是把 token 当成了字符串,但是会有转义的问题,当 token 中包含 " 时生成的代码为 ""hello""

下面先来看一下新的 template1c.py 生成了什么样的代码:

没问题,跟预期的是一样的。再来看一下 render 的效果:

不知道你有没有发现,其实 {{ variable }} 不只支持变量,还支持表达式和运算符:

这个既可以说是个 BUG 也可以说是个特性😂, 看模板引擎是否打算支持这些功能了, 我们在这里是打算支持这些功能 ;)。

既然支持了 {{ }} 那么支持注释也就非常好实现了。

注释

打算支持的注释模板语法是 {# comments #} ,有了上面实现 {{ variable }} 的经验,实现注释是类似的代码 (可以直接在 github 上下载 template1d.py ):

效果:

至此,我们的模板引擎已经支持了变量和注释功能。 那么如何实现支持 if 语句和 for 循环的标签语法呢:

我将在 第二篇文章 中向你详细的讲解。敬请期待。

打赏支持我写出更多好文章,谢谢!

打赏作者

打赏支持我写出更多好文章,谢谢!

2 2 收藏 评论

关于作者:mozillazg

好好学习,天天向上。 个人主页 · 我的文章 · 1 ·   

相关文章

可能感兴趣的话题



直接登录
跳到底部
返回顶部