最近我的一个同事想开一个博客, 问我有没有什么建议. 在研究了一些静态站点生成器和博客引擎后, 我觉得 Hugo 是个很不错的选择. 然而, 我同事还有一些其它需求, 比如想让博客都有自定义 URL 以及自定义 CSS 主题. 虽然用 Hugo 也能实现这些要求, 但是我还是决定跳过学习使用 Hugo 这部分, 看看如果我同事已经有随时可用的 html 而且在 HTML 中写博客没有问题, 能否创建一个很简单的静态站点生成器.
接下来就教你使用 node.JS>= 8.11.x 创建自己的静态站点生成器. 我们首先设置项目:
- NPM init
- NPM i --save-exact bluebird chokidar fs-extra mustache
- mkdir src
- mkdir public
首先我们问一个问题 -- 为什么需要静态站点生成器? 答案是实际上你并不是很需要它. 如果你的博客流量很小, 只需要手工编写 HTML 页面然后发布就行了. 实际上, 在服务器端编程兴起之前, 大部分 web 发布都是通过这种方式完成的. 然而, 如果你已经有了一些页面和内容, 修改所有页面的共有部分 (比如页脚) 会很麻烦. 因此, 如果我们能有某种简单的模板引擎, 可以分离出共有内容并将其插入需要的位置, 那就再好不过了.
在讲解模板引擎前, 首先设置我们的网站. 我们会在项目根 src(当前网站所在的位置)和 public(包含我们生成的网站)下创建 2 个文件夹. 将 src 的内容复制到 public, 在你的项目根下创建如下 index.JS:
- const Promise = require("bluebird");
- const fse = require("fs-extra");
- Promise.resolve().then(async () => {
- await main();
- });
- const main = async() => {
- await generateSite();
- };
- const generateSite = async() => {
- await copyAssets();
- };
- const copyAssets = async() => {
- await fse.emptyDir("public");
- await fse.copy("src", "public");
- };
通过 node index.JS 运行该脚本, 然后坐等胜利的喜悦就完事儿了.
- const chokidar = require("chokidar");
- const main = async() => {
- await generateSite();
- watchFiles();
- };
- const watchFiles = () => {
- const watcher = chokidar.watch(
- [
- "src"
- ],
- {
- ignored: /(^|[/\])../, // chokidar will watch folders recursively
- ignoreInitial: false,
- }
- );
- watcher.on("change", async path => {
- console.log("changed" + path + ", recompiling");
- await generateSite();
- });
- // catch ctrl+c event and exit normally
- process.on("SIGINT", function() {
- watcher.close();
- });
- };
- const env = process.env.NODE_ENV || "dev";
- const main = async () => {
- console.log("Running app in" + env);
- await generateSite();
- if (env === "dev") {
- watchFiles();
- }
- };
- const fs = require("fs");
- const generateSite = async () => {
- await copyAssets();
- await buildContent();
- };
- const buildContent = async () => {
- const pages = await compilePages();
- await writePages(pages);
- };
- const compilePages = async () => {
- const partials = await loadPartials();
- const result = {};
- const pagesDir = path.join("src", "pages");
- const fileNames = await fs.readdirAsync(pagesDir);
- for (const fileName of fileNames) {
- const name = path.parse(fileName).name;
- const fileContent = await fs.readFileAsync(path.join(pagesDir, fileName));
- result[name] = Mustache.render(fileContent.toString(), {}, partials);
- }
- };
- const loadPartials = async () => {
- const result = {};
- const partialsDir = path.join("src", "partials");
- const fileNames = await fs.readdirAsync(partialsDir);
- for (const fileName of fileNames) {
- const name = path.parse(fileName).name;
- const content = await fs.readFileAsync(path.join(partialsDir, fileName));
- result[name] = content.toString();
- }
- return result;
- };
- const writePages = async pages => {
- for (const page of Object.keys(pages)) {
- await fs.writeFileAsync(path.join("public", page + ".html"), pages[page]);
- }
- };
来源: http://www.qdfuns.com/article/51116/dbd649de78446a2845e3237bf707028c.html