如何将Wordpress作为你的JAMStack网站的内容管理系统

200 阅读7分钟

万能的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 */
  1. 让我们从我们的博客中获取前100篇文章。如果我们的Wordpress博客少于100篇文章,我们就不需要再取了。
  2. X-WP-TotalPages 头部有关于我们还需要取多少页的信息。
  3. 我们创建一个获取承诺的数组,从第二页开始获取数据(我们已经获取了第一页)。
  4. Promise.all 允许我们从我们的 数组中传递第一个结果和所有后续结果。pagesToFetch
  5. 下一个承诺的调用。将所有结果转换为JSON。
  6. 最后,我们将所有的结果转换为一个数组,其中包含我们博客的所有文章数据。

下一个.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 */
    });
}
  1. Metalsmith插件的接口是(files, metalsmith, done) 。第一个参数是需要转换为HTML的一组文件。第二个参数是 Metalsmith 对象,它包含 Metalsmith 构建的所有元数据信息。参数三是一个完成的回调函数。这对异步操作特别有帮助。当你的插件完成时调用它。
  2. 一旦我们有了来自API调用的所有帖子(见上文),我们需要转换一些数据。首先,我们把Wordpress的permalinks改成Metalsmith可以使用的东西。我们使用Node的URL包来获取相对的URL(没有域名),并在文件系统中创建一个相对路径。注意,我们添加了index.html 。这样做,我们创建了很多文件夹,里面有一个HTML文件。静态网站的漂亮的URLs。
  3. 接下来,我们为文件对象创建键/值对。每个值都是我们之前检索到的post数组的一个条目。此外,我们添加一个布局标志(用于metalsmith-layouts )并设置内容(这也是metalsmith-layouts 需要正常工作的东西)。
  4. 之后,我们将该值存储在我们先前定义的相对路径名下。
  5. 一旦我们为所有的帖子做了这些,我们就调用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庞大的插件生态系统,这样的东西可能已经存在了。