万能的JAMStack给你带来了快速和安全的静态网站,而且有了像无头内容管理系统这样的东西,它们甚至变得很容易编辑!然而,每隔一段时间,你会发现自己面前的Wordpress博客有太多的文章(和太多害怕改变的作者!),无法合理地转移。但Wordpress也可以是无头的。事实上,Wordpress自己的托管服务只通过API使用其核心,编辑界面则是由闪亮的新Calypso提供。
使用静态网站生成器的一个最好的优点是,他们通常不关心你的内容来自哪里。因此,让我们使用花哨的Wordpress REST API,获取一些内容并制作静态网站吧
在这个例子中,我使用Metalsmith作为静态网站生成器。只是因为我每天都在用它工作。而且它很容易让新插件运行。但是其他的静态网站生成器也可以工作。例如,你可以使用Jekyll生成器。而且只要你的静态网站生成器知道如何使用JSON文件作为数据输入,你可以使用下面的代码样本,在预处理步骤中存储获取的输出。开始吧!
Wordpress的API#
每个Wordpress安装都有一个成熟的JSON API。这意味着你可以通过URL访问帖子和页面。这对我来说就像一个无头的CMS!如果你有一个正在运行的Wordpress安装,在主URL的末尾添加/wp-json/wp/v2/posts 。你很可能会得到一些输出!事实上,最新的10个帖子及其所有元数据都以易于消化的JSON格式呈现给你。
你很快就会注意到,每个条目中的author 字段只是一个数字。这就是Wordpress的数据结构。你需要为作者查找表格,而Wordpress没有为此提供API URL。然而,你可以添加一个秘密标志,以获得所有作者数据的传递,它被称为_embed 。
因此,有了
https://url-to-your-blog/wp-json/wp/v2/posts?_embed
你就有了你需要的所有数据
获取所有的帖子#
如果你有大量的帖子,那么下一个挑战将是获取所有的帖子。遗憾的是,这不是一个单一的请求就能完成的。你可以通过添加一个名为per_page 的新参数将获取的帖子数量增加到100个。
https://url-to-your-blog/wp-json/wp/v2/posts?_embed&per_page=100
但在这之后,你必须分页获取。有一个叫page 的参数,你可以选择你要检索的页面。有了这个参数,你可以选择递归,只要有东西可取就取。或者你可以检查Wordpress的自定义HTTP头信息,了解有多少页面需要获取。在这个例子中,我选择了后者。但要注意,你的服务器的 CORS 设置必须允许将这些头信息传递给你的客户端。总页数的自定义标头是X-WP-TotalPages 。
为了检索数据,我使用了isomorphic-fetch,它为Node和浏览器提供相同的fetch API。让我们来看看。
const fetch = require('isomorphic-fetch');
const mainURL = 'http://path-to-your-blog';
const apiURL = '/wp-json/wp/v2/posts';
const url = `${mainURL}${apiURL}?_embed&per_page=100`;
fetch(url) /* 1 */
.then(res => {
const noPages =
res.headers.get('X-WP-TotalPages'); /* 2 */
const pagesToFetch = new Array(noPages - 1)
.fill(0)
.map((el, id) =>
fetch(`${url}&page=${id+2}`)); /* 3 */
return Promise.all([res, ...(pagesToFetch)]); /* 4 */
})
.then(results =>
Promise.all(results.map(el => el.json()))) /* 5 */
.then(pages => [].concat(...pages)) /* 6 */
- 让我们从我们的博客中获取前100篇文章。如果我们的Wordpress博客少于100篇文章,我们就不需要再取了。
X-WP-TotalPages头部有关于我们还需要取多少页的信息。- 我们创建一个获取承诺的数组,从第二页开始获取数据(我们已经获取了第一页)。
Promise.all允许我们从我们的 数组中传递第一个结果和所有后续结果。pagesToFetch- 下一个承诺的调用。将所有结果转换为JSON。
- 最后,我们将所有的结果转换为一个数组,其中包含我们博客的所有文章数据。
下一个.then 调用将包括一个包含所有博客条目的数组。你可以将这些数据存储为JSON文件(如果你的静态网站生成器是不可扩展的),或者在我们的案例中。创建我们想要生成的实际页面数据。
将你的文章添加到Metalsmith#中
Metalsmith--像其他静态网站生成器一样--知道一个包含文件的源目录。最有可能是Markdown。这些文件然后被转换为HTML。然而,Metalsmith也允许从任何其他来源添加数据。操作文件阵列和添加新文件是非常容易的。你唯一需要知道的是,每个文件都需要一个唯一的键。它将被存储的URL或路径。每个条目的内容是一个对象,包含你想存储的所有数据。让我们来看看吧!
Wordpress metalsmith插件#
Metalsmith与插件一起工作。Metalsmith构建管道的每一次运行都会运行你定义的插件列表,就像Gulp那样。
让我们使用上面的代码样本,并将其扩展为一个Metalsmith插件。
const { URL } = require('url');
const wordpress = (url) => (files, smith, done) => { /* 1 */
fetch(url)
/* ... include code from above ...*/
.then(allPages => {
allPages.forEach(page => {
const relativeURL
= new URL(page.link).pathname; /* 2 */
const key = `./${relativeURL}/index.html`;
let value = page; /* 3 */
value.layout = 'post.hbs';
value.contents =
new Buffer(page.content.rendered, 'utf8');
files[key] = value; /* 4 */
});
done(); /* 5 */
});
}
- Metalsmith插件的接口是
(files, metalsmith, done)。第一个参数是需要转换为HTML的一组文件。第二个参数是 Metalsmith 对象,它包含 Metalsmith 构建的所有元数据信息。参数三是一个完成的回调函数。这对异步操作特别有帮助。当你的插件完成时调用它。 - 一旦我们有了来自API调用的所有帖子(见上文),我们需要转换一些数据。首先,我们把Wordpress的permalinks改成Metalsmith可以使用的东西。我们使用Node的URL包来获取相对的URL(没有域名),并在文件系统中创建一个相对路径。注意,我们添加了
index.html。这样做,我们创建了很多文件夹,里面有一个HTML文件。静态网站的漂亮的URLs。 - 接下来,我们为文件对象创建键/值对。每个值都是我们之前检索到的post数组的一个条目。此外,我们添加一个布局标志(用于
metalsmith-layouts)并设置内容(这也是metalsmith-layouts需要正常工作的东西)。 - 之后,我们将该值存储在我们先前定义的相对路径名下。
- 一旦我们为所有的帖子做了这些,我们就调用
done()回调,以结束我们的插件过程。
完美。在短短的几行代码中,我们告诉 Metalsmith 用我们从 API 中获取的文件来扩展它已经转化的文件。这使得Metalsmith变得非常强大,因为你不再被束缚在一个单一的CMS上了。事实上,你可以连接到大量的新的和传统的内容管理系统,并仍然产生一个输出。很好!
Metalsmith构建管道#
我们想用一个非常简单的Metalsmith构建管道来使用我们的新插件。我们使用的不多,只有一个布局插件,它建立在Handlebars的基础上,将我们的内容挤压成语义上更正确的东西。
const Metalsmith = require('metalsmith');
const layouts = require('metalsmith-layouts');
/** the plug-in from above **/
Metalsmith('.')
.use(wordpress(apiURL))
.use(layouts({
engine: 'handlebars'
}))
.source('./source')
.destination('./build')
.build((err) => {
if (err) throw err;
console.log('Finished');
});
这个管道从Wordpress的API中获取所有数据,然后通过metalsmith-layouts 。在我们调用build ,该管道就被实际执行了。运行这个文件,你会在你的文件系统中看到一个build 目录。
布局文件#
布局文件是一个handlebars文件,它定义了基本的HTML结构。contents 是指我们先前在Wordpress Metalsmith插件中定义的字段。其余的直接来自对象,包括_embedded 作者数据。这是很直接的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{title.rendered}}</title>
</head>
<body>
<h1>{{title.rendered}}</h1>
{{{contents}}}
<aside>
by {{_embedded.author.0.name}}
</aside>
</body>
</html>
接下来的步骤#
很好!在我熟悉了Wordpress的API之后,获取所有的内容并从中创建静态网站是非常容易的。你可以在Github上找到一个样本库。让我知道你的想法。
下一步是创建一个小型的Wordpress插件(一个真正的插件,用PHP和所有的东西),使用发布钩子来自动启动你的持续集成系统。但是,了解Wordpress庞大的插件生态系统,这样的东西可能已经存在了。