HelloWorld中我似乎给了你一种感觉:eleventy这个工具就是用来把“文档”转化成“html”网页的。
这话对也不对,准确的来说,eleventy只是内置了一个把“文档”转化为“html代码段”的功能,如果你看过HelloWorld项目中,dist
目录中生成的index.html
的话,就明白我在说什么:
<h1>二流程序员的核心品质</h1>
<p>复制粘贴一把梭,增删改查小能手。</p>
<p>会写<code>Hello World</code>就敢说自己精通某个语言、框架</p>
那么我们现在回想一下流行的博客网站,就那种特别简单的博客站点,整个网站大概有以下几个比较突出的特点:
关键就在这个“共享的导航栏和外链与版权声明区”里:这一段页面元素几乎是整个站点所有页面都共享的。
如果你把这部分内容想象成C语言里的头文件,那它就是一段几乎要被项目中所有代码文件都要引用起来的stdio.h
所以我们先来看一看,怎么写这个“头文件”
这种静态站点里的“头文件”一般把它们叫做"template",不同的web框架中多多少少都支持这种内容,比如传统的Java世界里的JSP或者ASP .NET世界里的razor,就是这种东西,它们一般有两部分组成:
eleventy支持好几种template类型,不过比较常用的是一种叫Nunjucks的格式,这个文档格式挺简单,但要完全学习它还是要费一些时间的,不过不急,我们讲到哪里解释到哪里。
从最简单做起,我们现在重新再建个项目,就叫HelloTemplate吧,有了上一讲HelloWorld的经验,新建项目的步骤这里就不再赘述了。这个项目我们做两个页面,但:
新建项目的配置文件里,我们新增加一个配置项:
module.exports = function(config) {
return {
dir: {
+ includes: "_templates",
input: "src",
output: "dist"
}
};
}
${config.dir.includes}
配置的是头文件的搜索路径,它的值是一个相对于${config.dir.input}
的相对路径。也就是说,上面的配置项会告诉eleventy框架:请去./src/_templates
目录下去搜索头文件。
然后我们再新建一个头文件./src/_templates/global.njk
,文件扩展名.njk
是nunjucks模板文件的扩展名,内容如下:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{title}}</title>
</head>
<body>
<header>
<ul>
<li> <a href="/">index<a> </li>
<li> <a href="/page1">page1<a> </li>
<li> <a href="/page2">page2<a> </li>
</ul>
</header>
{{ content | safe }}
</body>
</html>
这个模板里有两个占位符:
{{ title }}
: 引擎会把一个名为title
的变量的值填充在这里{{ content | safe }}
: 引擎会找到一个名为content
的变量内容,然后再把这个变量的的值传给一个名为safe
的特殊函数,然后再把safe
函数返回的值填充在这里也就是说,其它文件如果使用这个template的话,必须提供这两个变量:title
和content
。而至于safe
这个函数,是随eleventy框架自带的,我们后面再解释它有什么作用。
我们在框架里还提供了一个简单的“导航”:其实就是两个分别指向/page1
和/page2
的链接
接下来,我们创建两个文件src/page1.md
和src/page2.md
,内容随你,我是如下写的
# page1
这是页面一
# page2
这是页面二
再创建一个src/index.md
作为首页,内容如下:
# index
这是首页
因为三个页面内容基本一致,所以下面我只以page1.md
的改动为例。以下描述的改动都要同时应用在三个文件上。
现在问题是:怎么在page1.md
中引用头文件?我们上面也说了,引用头文件就需要提供两个必要的变量title
和content
,这两个变量怎么定义?
content
变量是一个引擎自动定义的变量:在引用头文件时,引擎会把当前文件的渲染(转化)结果放在一个名为content
的变量中。也就是说我们不需要手动定义content
变量title
这个变量就需要我们手动定义它的值了还有一个问题:如何在page1.md
中声明对src/_templates/global.njk
的引用?
答案如下:
+---
+layout: global.njk
+title: 页面一
+---
# page1
这是页面一
通过在文档头部指定layout
变量的值,来说明我们需要引用的头文件是global.njk
。这样引擎就会去${config.dir.includes}
,也就是src/_templates
目录下去找对应的文件,找到了,bingo!
通过在文档头部指定title
变量的值,引擎就知道了在渲染时,如何填充global.njk
中的<title>{{title}}</title>
文档本身的渲染内容,会被引擎定义在content
变量中,然后填充在global.njk
中的{{ content | safe }}
位置处
safe
函数有什么用呢?简单来说就是page1.md
转化后的内容是HTML代码段。如果用safe
函数进行处理的话,引擎出于安全考虑,会把所有内容进行转义。比如# page1
会先被引擎转化成<h1>page1</h1>
,然后默认会被再处理成<h1>page1</h1>
。而如果使用safe
函数进行处理的话,就不会被二次转义,而会保持HTML标签的样子。
所以说,safe
的意思是“这段内容可以被安全的渲染,请不要做额外的转义”,而不是说“请对这段内容做转义,以确保它安全”。。这怎么说呢,稍微有点反常识。
模板,template,头文件的基本概念通过这样一个简单的例子我们就说清楚了。template最迷人的地方在于:它是可以互相嵌套的!即template可以引用其它template,发挥无限想象力!