点击阅读原文,以得到更好的阅读体验javascript
本文采用的语言是NodeJS,须要读者有必定的CSS、HTML、JS基础阅读。css
像hexo、Jekyll等静态博客引擎的主要功能为将模板与数据经过程序进行拼接编译,生成能够浏览的网页。大量不一样的数据与相同的模板拼接便可造成展现的静态网页组。多个网页组相结合便可生成静态网站。html
本篇文章将要实现一个以Pug
模板引擎和Stylus
样式引擎来编译出一个静态网页的工具,其中的模板与数据相互分离,模板文件、数据、输出路径也能够进行设置。java
工程代码:github.com/Zainking/te…node
咱们想要实现的目标是一个将html模板和数据分离的静态网页产出工具。实际上这种东西的核心就是模板引擎。咱们选择使用Pug
模板引擎来实现功能;而且为了统一语法,使用类Python
的缩进语法书写样式,咱们同时使用Stylus
样式引擎书写样式。 咱们将每份数据保存在json文件中,将模板和样式也分别写好放置在文件中,在程序运行时统一读取编译,再将编译后的字符串保存在html文件中便可完成需求。git
下面是一个Pug语法实例github
doctype html
html(lang="en")
head
title= pageTitle
script(type='text/javascript').
if (foo) bar(1 + 5)
body
h1 Pug - node template engine
#container.col
if youAreUsingPug
p You are amazing
else
p Get on it!
p.
Pug is a terse and simple templating language with a
strong focus on performance and powerful features.
复制代码
编译成为下面的Html:web
<!DOCTYPE html>
<html lang="en">
<head>
<title>Pug</title>
<script type="text/javascript">
if (foo) bar(1 + 5)
</script>
</head>
<body>
<h1>Pug - node template engine</h1>
<div id="container" class="col">
<p>You are amazing</p>
<p>Pug is a terse and simple templating language with a strong focus on performance and powerful features.</p>
</div>
</body>
</html>
复制代码
这是它的语法功能。除此以外,模板引擎的最大的用处就是在模板之中填充数据。对于Pug
而言,它会在编译时将全部模板字符串内容中的#{data}
用编译时传入的同名数据data
字符串进行替换。咱们能够在 pugjs.org/api/getting… 学习其详细语法。json
咱们用Pug
语法书写一个简历的模板文件放在template/index.pug。api
Stylus
是和Sass、Less类似的样式引擎,提供了更简便的CSS写法。 下面是一个语法示例:
body
font: 12px Helvetica, Arial, sans-serif;
a.button
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
复制代码
它会被Stylus
引擎编译成以下CSS
body {
font: 12px Helvetica, Arial, sans-serif;
}
a.button {
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
}
复制代码
咱们能够在 stylus-lang.com/ 学习其详细语法,咱们用Stylus
语法书写一个简历的样式文件放在template/style.styl。
须要注意,对于本需求,其实CSS引擎并不是是必要的。
至此,咱们完成了模板文件和样式文件的编写。咱们将须要填充的简历数据以json格式保存在data/0.json。注意:json中数据的明明应该与模板文件中数据名称一一对应。
编写readString
方法,使NodeJS能够读取文件中的字符串,以便读取模板文件:
function readString(path) {
return fs.readFileSync(path).toString()
}
复制代码
像这样使用它就能够读取到刚刚编写的模板文件里面的字符串了:
const pugStr = readString(pugPath)
const stylStr = readString(stylPath)
复制代码
同时由于数据是json格式,因此能够直接用require
引入:
const data = require(dataPath)
复制代码
注意,为了后期修改方面,咱们将pugPath
, stylPath
, dataPath
, outputPath
(输出文件夹路径)都保存到了配置文件中。
至此,咱们完成了模板、样式和数据的读取。
暂且先把目标定为将全部样式和模板、数据都打包到一个html中去。 先使用Stylus引擎编译Stylus字符串,此时咱们实现stylus2CssAsync
方法将Stylus的编译过程Promise化
,这样以后写代码能够用async / await
语法,会比较好看。
function stylus2CssAsync(stylStr) {
return new Promise(resolve => stylus.render(stylStr, (err, css) => resolve(css)))
}
复制代码
以后先调用这个方法编译Stylus字符串,再将编译结果和数据一同放入模板字符串。
const __style = await stylus2CssAsync(stylStr)
const html = pug.render(pugStr, { ...data, __style })
复制代码
这样咱们就能够得到所需求的html字符串了。
得到了网页对应的html字符串,咱们就能够编写对应的output
函数来输出字符串到文件了:
function output(path, data) {
fs.mkdirSync(path)
fs.writeFileSync(`${path}/index.html`, data)
}
复制代码
这个时候在根目录下执行node index.js
,就能够看见生成了dist目录,并在目录下生成了对应的index.html文件。
可是若是你想再修改一下模板,从新生成文件,这个时候就会报错:由于以前的目录已经存在,无法再从新生成一个同名目录了。这个时候咱们能够采用编译时比较通用的手段:先删除以前存在的产物目录。这个时候咱们就须要一个递归删除
函数:
function delDir(path) {
let files = [];
if (fs.existsSync(path)) {
files = fs.readdirSync(path);
files.forEach((file, index) => {
let curPath = path + "/" + file;
if (fs.statSync(curPath).isDirectory()) {
delDir(curPath); //递归删除文件夹
} else {
fs.unlinkSync(curPath); //删除文件
}
});
fs.rmdirSync(path);
}
}
复制代码
完成delDir
函数以后,将它放到output
的开始:
function output(path, data) {
delDir(path)
fs.mkdirSync(path)
fs.writeFileSync(`${path}/index.html`, data)
}
复制代码
这样咱们就完成了最终的输出函数。
至此,咱们成功生成了产物dist/index.html
,用浏览器便可运行。此时能够过修改数据随意产出不一样的网页。也能够经过修改模板来生成新的网页。咱们最初的需求大体完成。
读完这篇文章,你能够尝试从这些方面进行拓展:
a
标签尝试将网页组进行组合,造成静态网站。markdown
等富文本展示工具,来在你的数据中书写富文本并在网页中展示。uglifyjs
之类的工具对编译产物进行压缩混淆。Pug
那样拥有本身的一套语法,而且编译成html的模板引擎,你须要实现属于本身语法的语法解析器
,而后使用相似node-html-parser
的工具将其转化为html代码。hexo-deployer-git
插件,我在以前的文章基于githooks和node的自动部署环境搭建中提到过它。事实上后来想想以为直接用Shell脚本可能还来的更方便快捷一点。