50行代码实现Hexo等静态博客引擎的核心原理

点击阅读原文,以得到更好的阅读体验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.pugapi

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字符串

暂且先把目标定为将全部样式和模板、数据都打包到一个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文件

得到了网页对应的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,用浏览器便可运行。此时能够过修改数据随意产出不一样的网页。也能够经过修改模板来生成新的网页。咱们最初的需求大体完成。

Tips

读完这篇文章,你能够尝试从这些方面进行拓展:

  1. 本项目仓库中的代码有不少地方写的不够好,你能找出来吗?来提PR吧!
  2. 尝试批量编译不一样的数据,造成静态网页组。
  3. 尝试编写不一样的模板,用a标签尝试将网页组进行组合,造成静态网站。
  4. 尝试引入markdown等富文本展示工具,来在你的数据中书写富文本并在网页中展示。
  5. 尝试开源你的项目,引导更多人为你的项目产出模板(对应的模式就是Hexo的各类主题)。
  6. 研究一下模板引擎是如何工做的,试着写一个本身的模板引擎。
  7. 使用uglifyjs之类的工具对编译产物进行压缩混淆。

相关仓库

  1. 50行实现一个模板引擎的核心原理 在这个仓库中,我实现了一个不改变html语法的模板引擎,若是想要实现一个像Pug那样拥有本身的一套语法,而且编译成html的模板引擎,你须要实现属于本身语法的语法解析器,而后使用相似node-html-parser的工具将其转化为html代码。
  2. 自动部署某个文件夹内的静态文件到 Github Pages 相似于一个通用的 hexo-deployer-git 插件,我在以前的文章基于githooks和node的自动部署环境搭建中提到过它。事实上后来想想以为直接用Shell脚本可能还来的更方便快捷一点。
相关文章
相关标签/搜索