我以前的文章讨论了为什么应该或不应该考虑使用静态网站生成器的原因。 总而言之,静态网站生成器会通过Markdown文件中通常包含的模板和原始数据来构建仅HTML页面文件。 它提供了CMS的一些优势,而没有托管,性能和安全性开销。
静态站点可能适用于一系列项目,包括:
- 小型网站或个人博客。 站点只有几十页,不经常发布并且只有一两个作者的网站可能是理想的选择。
- 技术文档,例如REST API。
- 需要一系列网页视图的应用程序原型。
- 电子书— Markdown文件可以转换为PDF或其他格式以及HTML。
本质上,静态站点生成器是构建工具。 您可以像使用Grunt或Gulp一样使用它来运行任务或项目支架。
为什么选择金属匠?
毫无疑问,静态站点冠军是Jekyll,这是2008年启动的Ruby项目。使用Jekyll不一定需要Ruby专业知识,但它会有所帮助。 幸运的是,对于大多数流行的语言,有各种各样的开源静态站点生成器 。 JavaScript选项包括Hexo , Harp和Assemble 。 您也可以使用诸如Gulp之类的构建工具来简化项目。
我选择本教程的Metalsmith是因为:
- 不针对特定项目类型,例如博客
- 支持多种模板和数据格式选项
- 轻巧
- 很少有依赖
- 使用模块化结构
- 提供了一个简单的插件架构,并且
- 很容易上手。
本教程已建立了一个演示网站 。 它不会获得任何设计大奖,但可以说明基本概念。 Metalsmith构建代码可以从GitHub存储库中检查和安装。 或者,您可以按照此处的说明创建自己的基本站点。
我已经使用Metalsmith了几次,请不要以为这是构建每个静态站点的明确方法!
安装Metalsmith
确保已安装Node.js (例如, 使用nvm ),然后创建一个新的项目目录(例如project
并初始化package.json
文件:
cd project && cd project
npm init -y
现在安装Metalsmith和我们将用来构建站点的各种插件。 这些是:
- metalsmith-assets —在您的Metalsmith版本中包含静态资产
- metalsmith-browser- sync-将BrowserSync集成到您的工作流程中
- metalsmith-collections —将文件集合添加到全局元数据
- metalsmith-feed-为集合生成RSS feed
- metalsmith-html-minifier-使用kangax / html-minifier最小化HTML文件
- Metalsmith-in-place —在源文件中渲染模板语法
- metalsmith-layouts-将布局应用于源文件
- metalsmith-mapsite-生成一个sitemap.xml文件
- metalsmith-markdown —转换markdown文件
- metalsmith-permalinks-将自定义的永久链接模式应用于文件
- metalsmith-publish —增加了对草稿,私人和未来职位的支持
- metalsmith-word-count —计算HTML文件中所有段落的字数/平均阅读时间
npm install --save-dev metalsmith metalsmith-assets metalsmith-browser-sync metalsmith-collections metalsmith-feed metalsmith-html-minifier metalsmith-in-place metalsmith-layouts metalsmith-mapsite metalsmith-markdown metalsmith-permalinks metalsmith-publish metalsmith-word-count handlebars
项目结构
我们将在项目内的源( src
)和构建( build
)目录中使用以下结构。
您可以按照以下说明创建示例文件,也可以直接从demos src目录中复制它们。
页数
页面Markdown文件包含在src/html
。 每个网站部分可以包含一个子目录级别,即
-
src/html/start
—以特定顺序描述项目的页面 -
src/html/article
—各种文章按时间倒序排列 -
src/html/contact
—单个联系页面
每个目录都包含一个index.md
文件,这是该部分的默认页面。 其他页面可以使用任何唯一名称。
构建过程会将这些文件转换为基于目录的永久链接,例如
-
src/html/start/index.md
变成/start/index.html
-
src/html/start/installation.md
变为/start/installation/index.html
每个Markdown文件在---
标记之间的顶部提供称为“前题”的内容和元信息,例如
---
title: My page title
description: A description of this page.
layout: page.html
priority: 0.9
date: 2016-04-19
publish: draft
---
This is a demonstration page.
## Example title
Body text.
大多数前端是可选的,但您可以设置:
-
priority
:介于0(低)和1(高)之间的数字,我们将使用它来排序菜单和定义XML网站地图。 -
publish
:可以设置为draft
,private
或将来的日期,以确保直到需要时才发布。 -
date
:文章的日期。 如果未设置,我们将使用以后的任何发布日期或文件创建日期。 -
layout
:要使用的HTML模板。
范本
HTML页面模板包含在src/template
。 定义了两个模板:
尽管支持其他选项,但仍使用Handlebars模板系统。 一个典型的模板需要一个{{{ contents }}}
标签来包含页面内容以及诸如{{ title }}
类的所有前值:
<!DOCTYPE html>
<html lang="en">
<head>
{{> meta }}
</head>
<body>
{{> header }}
<main>
<article>
{{#if title}}
<h1>{{ title }}</h1>
{{/if}}
{{{ contents }}}
</article>
</main>
{{> footer }}
</body>
</html>
对{{> meta }}
, {{> header }}
和{{> footer }}
引用是不完整的……
部分
部分(或HTML片段文件)包含在src/partials
。 这些主要用于模板中,但也可以使用以下代码包含在内容页面中:
{{> partialname }}
其中, partialname
是src/partials
目录中文件的名称。
静态资产
静态资源(例如图像,CSS和JavaScript文件)包含在src/assets
。 所有文件和子目录将原样复制到网站的根目录。
自定义插件
建立站点所需的自定义插件包含在lib
目录中。
建立目录
该网站将建立在build
目录中。 我们将以两种方式构建站点:
- 开发模式:不会缩小HTML并将启动测试Web服务器。
- 生产模式:如果将
NODE_ENV
设置为production
,那么将擦除build
目录并生成最终的最小化文件。
定义第一个构建文件
可以在项目目录的根目录中创建一个名为build.js
的基本示例:
// basic build
'use strict';
var
metalsmith = require('metalsmith'),
markdown = require('metalsmith-markdown'),
ms = metalsmith(__dirname) // the working directory
.clean(true) // clean the build directory
.source('src/html/') // the page source directory
.destination('build/') // the destination directory
.use(markdown()) // convert markdown to HTML
.build(function(err) { // build the site
if (err) throw err; // and throw errors
});
使用node ./build.js
运行此node ./build.js
,将在build
目录中创建一个静态站点。 Markdown将被解析为HTML,但由于我们在构建过程中未包含模板,因此无法使用。
Metalsmith插件
从表面上看,Metalsmith构建文件看起来类似于Gulp中使用的文件(尽管它不使用流)。 通过use
任何适当的参数将插件传递给Metalsmith use
方法来调用插件。 插件本身必须返回另一个接受三个参数的函数:
- 一个
files
数组,其中包含有关每个页面的信息 - 包含全局信息(例如元数据)的
metalsmith
对象,以及 - 插件完成工作后必须调用的
done
函数
这个简单的示例将所有元和页面信息记录到控制台(可以在build.js
定义):
function debug(logToConsole) {
return function(files, metalsmith, done) {
if (logToConsole) {
console.log('\nMETADATA:');
console.log(metalsmith.metadata());
for (var f in files) {
console.log('\nFILE:');
console.log(files[f]);
}
}
done();
};
};
可以更新Metalsmith构建代码以使用此插件:
ms = metalsmith(__dirname) // the working directory
.clean(true) // clean the build directory
.source('src/html/') // the page source directory
.destination('build/') // the destination directory
.use(markdown()) // convert Markdown to HTML
.use(debug(true)) // *** NEW *** output debug information
.build(function(err) { // build the site
if (err) throw err; // and throw errors
});
该调试功能可以帮助您创建自己的自定义插件,但是您可能需要的大多数功能已经编写完毕-Metalsmith网站上的插件列表很长。
进行更好的构建
下面说明了演示站点构建文件的关键部分。
如果将NODE_ENV
环境变量设置为production
(在Mac / Linux上为export NODE_ENV=production
或在Windows上为set NODE_ENV=production
), NODE_ENV
名为devBuild
的变量设置为true
:
devBuild = ((process.env.NODE_ENV || '').trim().toLowerCase() !== 'production')
主目录在dir
对象中定义,因此我们可以重复使用它们:
dir = {
base: __dirname + '/',
lib: __dirname + '/lib/',
source: './src/',
dest: './build/'
}
Metalsmith和插件模块已加载。 注意:
- 仅在创建开发版本时才需要出色的Browsersync测试服务器
- 通过引用的HTML minifier模块
htmlmin
创建生产版本时,才需要 - 定义了三个自定义插件:
setdate
,moremeta
和debug
(下面将详细介绍)
metalsmith = require('metalsmith'),
markdown = require('metalsmith-markdown'),
publish = require('metalsmith-publish'),
wordcount = require("metalsmith-word-count"),
collections = require('metalsmith-collections'),
permalinks = require('metalsmith-permalinks'),
inplace = require('metalsmith-in-place'),
layouts = require('metalsmith-layouts'),
sitemap = require('metalsmith-mapsite'),
rssfeed = require('metalsmith-feed'),
assets = require('metalsmith-assets'),
htmlmin = devBuild ? null : require('metalsmith-html-minifier'),
browsersync = devBuild ? require('metalsmith-browser-sync') : null,
// custom plugins
setdate = require(dir.lib + 'metalsmith-setdate'),
moremeta = require(dir.lib + 'metalsmith-moremeta'),
debug = consoleLog ? require(dir.lib + 'metalsmith-debug') : null,
用适用于每个页面的信息定义siteMeta
对象。 重要值是domain
和rootpath
,它们是根据开发或生产版本设置的:
siteMeta = {
devBuild: devBuild,
version: pkg.version,
name: 'Static site',
desc: 'A demonstration static site built using Metalsmith',
author: 'Craig Buckler',
contact: 'https://twitter.com/craigbuckler',
domain: devBuild ? 'http://127.0.0.1' : 'https://rawgit.com', // set domain
rootpath: devBuild ? null : '/sitepoint-editors/metalsmith-demo/master/build/' // set absolute path (null for relative)
}
还定义了templateConfig
对象来设置模板默认值。 这将由metalsmith-in-place
metalsmith-layouts
插件和metalsmith-layouts
插件使用,它们可使用Handlebars启用页内和模板渲染:
templateConfig = {
engine: 'handlebars',
directory: dir.source + 'template/',
partials: dir.source + 'partials/',
default: 'page.html'
}
Metalsmith对象现在像以前一样启动,但是我们还将siteMeta
对象传递给metadata
方法,以确保每个页面上的信息都可用。 因此,我们可以在任何页面中引用诸如{{ name }}
项目来获取站点名称。
var ms = metalsmith(dir.base)
.clean(!devBuild) // clean build before a production build
.source(dir.source + 'html/') // source directory (src/html/)
.destination(dir.dest) // build directory (build/)
.metadata(siteMeta) // add meta data to every page
我们的第一个插件调用调用metalsmith-publish
,它删除其前值publish
值设置为draft
, private
或将来日期的任何文件:
.use(publish()) // draft, private, future-dated
setdate
是lib / metalsmith-setdate.js中包含的自定义插件。 通过在可能的情况下回退到publish
日期或文件创建时间,即使在开头没有定义任何文件,它也可以确保为每个文件设置一个“日期”值:
.use(setdate()) // set date on every page if not set in front-matter
metalsmith-collections
是最重要的插件之一,因为它根据页面在源目录中的位置或其他因素将每个页面分配到一个类别或分类法。 它可以使用date
或priority
等最重要的内容对文件进行重新排序,并允许您为该集合设置自定义元数据。 该代码定义:
-
src/html/start
目录中每个文件的启动集合。 它按照文件前面的priority
值对它们进行排序。 -
src/html/article
目录中每个文件的文章集合。 它按date
按相反的时间顺序排序 - 每个名为
index.*
默认页面的页面集合。 它按照文件前面的priority
值对它们进行排序。
.use(collections({ // determine page collection/taxonomy
page: {
pattern: '**/index.*',
sortBy: 'priority',
reverse: true,
refer: false
},
start: {
pattern: 'start/**/*',
sortBy: 'priority',
reverse: true,
refer: true,
metadata: {
layout: 'article.html'
}
},
article: {
pattern: 'article/**/*',
sortBy: 'date',
reverse: true,
refer: true,
limit: 50,
metadata: {
layout: 'article.html'
}
}
}))
接下来是Markdown到HTML的转换,接着是metalsmith-permalinks
插件,该插件定义了构建的目录结构。 注意:mainCollection
是通过下面的moremeta
为每个文件设置的:
.use(markdown()) // convert Markdown
.use(permalinks({ // generate permalinks
pattern: ':mainCollection/:title'
}))
metalsmith-word-count
计数文章中的单词数,并计算大约需要花费多长时间。 参数{ raw: true }
仅输出数字:
.use(wordcount({ raw: true })) // word count
moremeta
是lib / metalsmith-moremeta.js中包含的另一个自定义插件。 它将其他元数据附加到每个文件:
-
root
:到根目录的绝对或计算的相对文件路径 -
isPage
:为名为index.*
默认节页面设置trueindex.*
-
mainCollection
:主要集合名称,start
或article
-
layout
:如果未设置,则可以从主集合的元数据中确定布局模板 -
navmain
:顶级导航对象的数组 -
navsub
:二级导航对象的数组
插件代码相对复杂,因为它可以处理导航。 如果您需要更简单的层次结构,则有更简单的选项。
.use(moremeta()) // determine root paths and navigation
metalsmith-in-place
和metalsmith-layouts
插件分别控制页面内和模板布局。 传递了上面定义的相同templateConfig
对象:
.use(inplace(templateConfig)) // in-page templating
.use(layouts(templateConfig)); // layout templating
如果设置了htmlmin
(在生产版本中),我们可以缩小HTML:
if (htmlmin) ms.use(htmlmin()); // minify production HTML
debug
是lib / metalsmith-debug.js中包含的最后一个自定义插件。 它类似于上述debug
功能:
if (debug) ms.use(debug()); // output page debugging information
Browsersync测试服务器已启动,因此我们可以测试开发版本。 如果您从未使用过它,那么它看起来就像是魔术:每次进行更改时,网站都会神奇地刷新,并且在您滚动或浏览该网站时,两个或多个浏览器中的视图会同步:
if (browsersync) ms.use(browsersync({ // start test server
server: dir.dest,
files: [dir.source + '**/*']
}));
最后,我们可以使用:
-
metalsmith-mapsite
生成XML网站地图 -
metalsmith-feed
生成RSS提要,其中包含文章集中的页面 -
metalsmith-assets
直接从src/assets
复制文件和目录以进行build
而无需修改。
ms
.use(sitemap({ // generate sitemap.xml
hostname: siteMeta.domain + (siteMeta.rootpath || ''),
omitIndex: true
}))
.use(rssfeed({ // generate RSS feed for articles
collection: 'article',
site_url: siteMeta.domain + (siteMeta.rootpath || ''),
title: siteMeta.name,
description: siteMeta.desc
}))
.use(assets({ // copy assets: CSS, images etc.
source: dir.source + 'assets/',
destination: './'
}))
剩下的就是创建站点的最后一个.build()
步骤:
.build(function(err) { // build
if (err) throw err;
});
完成后,您可以运行node ./build.js
再次构建您的静态站点。
陷阱
我在建立一个简单的Metalsmith网站方面学到了很多,但是要注意以下问题:
不兼容的插件
插件可能会与其他人冲突。 例如,计算相对根路径的metalsmith-rootpath不能与创建自定义构建目录结构的metalsmith-permalinks很好地配合。 我通过在lib / metalsmith-moremeta.js插件中编写自定义root
路径计算代码来解决此问题。
插件顺序至关重要
如果放置顺序错误,插件可能会相互依赖或冲突。 例如,必须在metalsmith布局之后调用生成RSS的metalsmith-feed插件,以确保不在页面模板内生成RSS XML。
Browsersync重建问题
当Browsersync运行并编辑文件时,将重新解析集合,但似乎保留了旧数据。 自定义的lib / metalsmith-moremeta.js插件可能是一个问题,但菜单和下一个/上一个链接不同步。 要修复它,请使用Ctrl / Cmd + C停止构建,然后重新启动构建。
您还需要吞咽吗?
那些使用任务管理器(例如Gulp)的人会注意到Metalsmith提供了熟悉的构建过程。 有一些用于CSS预处理的插件, 包括Sass , 图像缩小 , 文件串联 , uglification等。 对于简单的工作流程可能就足够了。
但是,Gulp具有更广泛的插件范围,并允许使用自动前缀对linting ,部署和PostCSS处理等复杂的构建活动。 有几个Gulp / Metalsmith集成插件,尽管我遇到了几个问题,但它们不是必需的,因为Gulp任务可以直接运行Metalsmith,例如
var
gulp = require('gulp'),
metalsmith = require('metalsmith'),
publish = require('metalsmith-publish'),
markdown = require('metalsmith-markdown');
// build HTML files using Metalsmith
gulp.task('html', function() {
var ms = metalsmith(dir.base)
.clean(false)
.source('src/html/')
.destination('build')
.use(publish())
.use(markdown())
.build(function(err) {
if (err) throw err;
});
});
此过程可防止上述的Browsersync重建问题。 请记住使用.clean(false)
以确保Metalsmith在其他任务处于活动状态时从不擦除build文件夹。
Metalsmith适合您吗?
如果您有简单或高度定制的网站要求,Metalsmith是理想的选择。 也许尝试使用一个文档项目并一次添加一个功能。 Metalsmith的功能不如Jekyll这样的替代产品完整,但并非如此。 您可能必须编写自己的插件,但是这样做的简便性为JavaScript开发人员带来了巨大的好处。
创建Metalsmith构建系统需要花费时间,而且我们还没有考虑HTML模板和部署所涉及的工作。 但是,一旦有了工作流程,添加,编辑和删除Markdown文件就变得非常简单。 它比使用CMS更容易,并且您拥有静态站点的所有好处 。
From: https://www.sitepoint.com/create-static-site-metalsmith/