在 WordPress 中创建无头 REST API

WordPress (WordPress.org) 是一个用于通用网站开发的开源 PHP CMS。它默认不是“无头”的;它与一个模板系统堆叠在一起。这意味着视图和数据是交织在一起的。然而,自 2015 年(WordPress 4.4)以来,REST API 基础设施已集成到 WordPress 核心中供开发人员使用,现在,如果你将 /wp-json/ 附加到你的网站 URL,就可以访问所有默认端点。你还可以扩展 WordPress REST API 并添加你自己的自定义端点。

因此,通过忽略视图,我们可以轻松地将 WordPress 用作“无头”REST API。你将在接下来的部分中了解如何实现这一点。为了加快开发过程,我们将安装以下 WordPress 插件:

如果你不想使用这些插件,可以创建自己的插件和元框。请查看如何在 https://developer.wordpress.org/plugins/metadata/custom-meta-boxes/ 创建自定义元框。另请查看如何在 https://developer.wordpress.org/plugins/intro/ 开发自定义插件。

有关 WordPress REST API 的更多信息,请访问 https://developer.wordpress.org/rest-api/。

要使用这些插件或你自己的插件开发和扩展 WordPress REST API,首先你需要下载 WordPress 并将程序安装到你的机器上。我们将在下一节中学习如何做到这一点。

安装 WordPress 并创建我们的第一个页面

我们可以通过几种方式安装和运行 WordPress:

本书将使用内置的 PHP 服务器,因为这是启动 WordPress 最简单的方法,并且如果我们需要在将来移动它,只要它在同一个端口(例如 localhost:4000)上运行,也会更容易。所以,让我们看看如何做到这一点:

  1. 创建一个目录(也要使其可写),并在其中下载并解压 WordPress。你可以从 https://wordpress.org/ 下载 WordPress。你应该在解压后的 WordPress 目录中看到一些 .php 文件以及 /wp-admin/、/wp-content/ 和 /wp-includes/ 目录。

  2. 通过 PHP Adminer 创建一个 MySQL 数据库(例如,nuxt-wordpress)。

  3. 导航到该目录,并使用内置 PHP 运行 WordPress,如下所示:

    $ php -S localhost:4000
  4. 将你的浏览器指向 localhost:4000,并使用所需的 MySQL 凭据(数据库名称、用户名和密码)以及你的 WordPress 用户帐户信息(用户名、密码和电子邮件地址)安装 WordPress。

  5. 使用你的用户凭据登录 WordPress 管理后台 localhost:4000/wp-admin/,并在“页面”标签下创建一些主要页面(首页、关于、项目、联系方式)。

  6. 从“外观”下导航到“菜单”,并通过在“菜单名称”输入字段中添加 menu-main 来创建站点导航。

  7. 选择“添加菜单项”下显示的所有页面(联系方式、关于、项目、首页),然后单击“添加到菜单”将它们添加到 menu-main 作为导航项。你可以拖放和排序这些项目,使它们按以下顺序显示:首页、关于、项目、联系方式。然后,单击“保存菜单”按钮。

  8. (可选)在 “设置” 下的 “固定链接” 中,将 WordPress 固定链接从 “朴素” 选项更改为 “自定义结构”(例如,值为 /%postname%/)。

  9. 下载我们前面提到的插件,并将它们解压到 /wp-content/ 目录中的 /plugins/ 目录中。然后,通过管理后台激活它们。

如果你检查 nuxt-wordpress 数据库中的 wp_options 表,你应该看到端口 4000 已成功记录在 siteurl 和 home 字段中。因此,从现在开始,你可以随意移动你的 WordPress 项目目录,只要你使用内置的 PHP 服务器在这个端口上运行它即可。

虽然我们在 WordPress 中拥有主要页面和导航的数据,但我们仍然需要 “项目” 页面的子页面的数据。我们可以将它们添加到 “页面” 标签下,然后将它们附加到 “项目” 页面。但是这些页面将共享相同的内容类型(在 WordPress 中称为“文章类型”)—— “页面” 文章类型。最好将它们组织在一个单独的文章类型中,以便更轻松地管理它们。我们将在下一节中了解如何在 WordPress 中创建自定义文章类型。

有关 WordPress 安装过程的更多详细信息,请访问 https://wordpress.org/support/article/how-to-install-wordpress/。

在 WordPress 中创建自定义文章(post)类型

我们可以在任何 WordPress 主题的 functions.php 文件中创建自定义文章类型。然而,由于我们不打算使用 WordPress 模板系统来呈现内容视图,我们可以从 WordPress 提供的默认主题扩展一个子主题。然后,我们只需在“外观”下的 “主题” 中激活该子主题。我们将使用 “Twenty Nineteen” 主题来扩展我们的子主题,并从那里创建自定义文章类型。让我们开始吧:

  1. 在 /themes/ 目录下创建一个名为 twentynineteen-child 的目录,并创建一个包含以下内容的 style.css 文件:

    // wp-content/themes/twentynineteen-child/style.css
    /*
    Theme Name: Twenty Nineteen Child
    Template: twentynineteen
    Text Domain: twentynineteenchild
    */
    @import url("../twentynineteen/style.css");

    Theme Name、Template 和 Text Domain 是扩展主题所需的最低限度的头部注释,后跟导入其父主题的 style.css 文件。这些头部注释必须放在文件的顶部。

    如果你想在这个子主题中包含更多头部注释,请访问 https://developer.wordpress.org/themes/advanced-topics/child-themes/。

  2. 在 /twentynineteen-child/ 目录中创建一个 functions.php 文件,并使用以下格式和 WordPress 的 register_post_type 函数创建自定义文章类型:

    // wp-content/themes/twentynineteen-child/functions.php
    function create_something () {
        register_post_type('<name>', <args>);
    }
    add_action('init', 'create_something');

    因此,要添加我们的自定义文章类型,只需使用 project 作为类型名称并提供一些参数:

    // wp-content/themes/twentynineteen-child/functions.php
    function create_project_post_type () {
        register_post_type('project', $args);
    }
    add_action('init', 'create_project_post_type');

    我们可以为自定义文章类型 UI 添加标签和我们想要支持的内容字段,如下所示:

    $args = [
        'labels' => [
            'name' => __('Project (Pages)'),
            'singular_name' => __('Project'),
            'all_items' => 'All Projects'
        ],
        //...
        'supports' => ['title', 'editor', 'thumbnail', 'pageattributes'],
    ];

    有关 register_post_type 函数的更多信息,请访问 https://developer.wordpress.org/reference/functions/register_post_type/。

    有关自定义文章类型 UI 的更多信息,请访问 https://wordpress.org/plugins/custom-post-type-ui/。

  3. (可选)我们还可以为这个自定义文章类型添加对分类和标签的支持,如下所示:

    'taxonomies' => [
        'category',
        'post_tag'
    ],

    然而,这些是全局的分类和标签实例,这意味着它们与其他文章类型(如 “页面” 和 “文章” 文章类型)共享。因此,如果你只想为 “项目” 文章类型指定特定的分类,请使用以下代码:

    // wp-content/themes/twentynineteen-child/functions.php
    add_action('init', 'create_project_categories');
    function create_project_categories() {
        $args = [
            'label' => __('Categories'),
            'has_archive' => true,
            'hierarchical' => true,
            'rewrite' => [
                'slug' => 'project',
                'with_front' => false
            ],
        ];
        $postTypes = ['project'];
        $taxonomy = 'project-category';
        register_taxonomy($taxonomy, $postTypes, $args);
    }
  4. (可选)如果你觉得 Gutenberg 难以使用,完全禁用所有文章类型的 Gutenberg 也是一个好主意:

    // wp-content/themes/twentynineteen-child/functions.php
    add_filter('use_block_editor_for_post', '__return_false', 10);
    add_filter('use_block_editor_for_post_type', '__return_false', 10);
  5. WordPress 管理后台激活子主题,并开始向 “项目” 标签添加项目类型的页面。

你会注意到,你可以用来向项目页面添加内容的内容字段(标题、编辑器、缩略图、页面属性)非常有限。我们需要更具体的内容字段,例如用于添加多个项目图像和全屏图像的内容字段。这与首页遇到的问题相同,因为我们也需要另一个内容字段来添加多个幻灯片图像。要添加更多这些内容字段,我们将需要自定义元框。你可以使用 ACF 插件或创建自己的自定义元框,并将它们包含在 functions.php 文件中或将它们创建为插件。或者,你可以使用另一个不同的元框插件,例如 Meta Box (https://metabox.io/)。这完全取决于你。

创建自定义内容字段并向每个项目页面添加所需内容后,你可以扩展 WordPress REST API 以用于项目页面、主要页面和导航。我们将在下一节中学习如何做到这一点。

扩展 WordPress REST API

WordPress REST API 可以通过 /wp-json/ 访问,它是附加到你的网站 URL 的入口路由。例如,你可以通过在浏览器中访问 localhost:4000/wp-json/ 来查看所有其他可用的路由。你将看到每个路由中可用的端点,因为它们可以是 GET 或 POST 端点。例如,/wp-json/wp/v2/pages 路由有一个用于列出页面的 GET 端点和一个用于创建页面的 POST 端点。你可以在 https://developer.wordpress.org/rest-api/reference/ 找到关于这些默认路由和端点的更多信息。

然而,如果你有自定义文章类型和自定义内容字段,那么你需要自定义路由和端点。我们可以通过在 functions.php 文件中使用 register_rest_route 函数注册它们来创建这些自定义版本,如下所示:

add_action('rest_api_init', function () {
    $args = [
        'methods' => 'GET',
        'callback' => '<do_something>',
    ];
    register_rest_route(<namespace>, <route>, $args);
});

让我们学习如何扩展 WordPress REST API:

  1. 创建一个全局命名空间和用于获取导航和单个页面的端点:

    // wp-content/themes/twentynineteen-child/functions.php
    $namespace = 'api/v1/';
    add_action('rest_api_init', function () use ($namespace) {
        $route = 'menu';
        $args = [
            'methods' => 'GET',
            'callback' => 'fetch_menu',
        ];
        register_rest_route($namespace, $route, $args);
    });
    add_action('rest_api_init', function () use ($namespace) {
        $route = 'page/(?P<slug>[a-zA-Z0-9-]+)';
        $args = [
            'methods' => 'GET',
            'callback' => 'fetch_page',
        ];
        register_rest_route($namespace, $route, $args);
    });

    请注意,我们通过在匿名函数中使用 PHP use 关键字将全局命名空间传递给每个 add_action 块。有关 PHP use 关键字和匿名函数的更多信息,请访问 https://www.php.net/manual/en/functions.anonymous.php。

    有关 WordPress 的 register_rest_route 函数的更多信息,请访问 https://developer.wordpress.org/reference/functions/register_rest_route/。

  2. 创建用于获取单个项目页面和列出项目页面的端点:

    // wp-content/themes/twentynineteen-child/functions.php
    add_action('rest_api_init', function () use ($namespace) {
        $route = 'project/(?P<slug>[a-zA-Z0-9-]+)';
        $args = [
            'methods' => 'GET',
            'callback' => 'fetch_project',
        ];
        register_rest_route($namespace, $route, $args);
    });
    add_action('rest_api_init', function () use ($namespace) {
        $route = 'projects/(?P<page_number>\d+)';
        $args = [
            'methods' => 'GET',
            'callback' => 'fetch_projects',
        ];
        register_rest_route($namespace, $route, $args);
    });
  3. 创建一个 fetch_menu 函数来获取 menu-main 导航项:

    // wp-content/themes/twentynineteen-child/functions.php
    function fetch_menu ($data) {
        $menu_items = wp_get_nav_menu_items('menu-main');
        if (empty($menu_items)) {
            return [];
        }
        return $menu_items;
    }

    我们使用 WordPress 的 wp_get_nav_menu_items 函数来帮助我们获取导航。

    有关 wp_get_nav_menu_items 函数的更多信息,请访问 https://developer.wordpress.org/reference/functions/wp_get_nav_menu_items/。

  4. 创建一个 fetch_page 函数来按别名(或路径)获取页面:

    // wp-content/themes/twentynineteen-child/functions.php
    function fetch_page ($data) {
        $post = get_page_by_path($data['slug'], OBJECT, 'page');
        if (!count((array)$post)) {
            return [];
        }
        $post->slides = get_field('slide_items', $post->ID);
        return $post;
    }

    在这里,我们使用 WordPress 的 get_page_by_path 函数来获取页面。有关此函数的更多信息,请访问 https://developer.wordpress.org/reference/functions/get_page_by_path/。

    我们还使用 ACF 插件的 get_field 函数来获取附加到页面的幻灯片图像列表,然后将它们作为 slides 推送到 $post 对象中。有关此函数的更多信息,请访问 https://www.advancedcustomfields.com/resources/get_field/。

  5. 创建一个 fetch_project 函数来获取单个项目页面:

    // wp-content/themes/twentynineteen-child/functions.php
    function fetch_project ($data) {
        $post = get_page_by_path($data['slug'], OBJECT, 'project');
        if (!count((array)$post)) {
            return [];
        }
        $post->fullscreen = get_field('full_screen_image', $post->ID);
        $post->images = get_field('image_items', $post->ID);
        return $post;
    }

    同样,我们使用 WordPress 的 get_page_by_path 函数来获取页面,并使用 ACF 的 get_field 函数来获取附加到项目页面的图像(全屏图像和项目图像),然后将它们作为 fullscreen 和 images 推送到 $post 对象中。

  6. 创建一个 fetch_projects 函数来获取项目页面列表,每页 6 个项目:

    // wp-content/themes/twentynineteen-child/functions.php
    function fetch_projects ($data) {
        $paged = $data['page_number'] ? $data['page_number'] : 1;
        $posts_per_page = 6;
        $post_type = 'project';
        $args = [
            'post_type' => $post_type,
            'post_status' => ['publish'],
            'posts_per_page' => $posts_per_page,
            'paged' => $paged,
            'orderby' => 'date'
        ];
        $posts = get_posts($args);
        if (empty($posts)) {
            return [];
        }
        foreach ($posts as &$post) {
            $post->featured_image =
                get_the_post_thumbnail_url($post->ID);
        }
        return $posts;
    }

    在这里,我们使用 WordPress 的 get_posts 函数以及所需的参数来获取列表。有关此函数的更多信息,请访问 https://developer.wordpress.org/reference/functions/get_posts/。

    然后,我们循环遍历每个项目页面,并将它们的特色图像推送到 WordPress 的 get_the_post_thumbnail_url 函数中。有关此函数的更多信息,请访问 https://developer.wordpress.org/reference/functions/get_the_post_thumbnail_url/。

  7. 我们还需要计算数据(上一页码和下一页码),以便为项目页面制作分页。因此,不要只返回 $posts,而是将其作为 items 包含在以下带有分页数据的数组中:

    $total = wp_count_posts($post_type);
    $total_max_pages = ceil($total->publish / $posts_per_page);
    return [
        'items' => $posts,
        'total_pages' => $total_max_pages,
        'current_page' => (int)$paged,
        'next_page' => (int)$paged === (int)$total_max_pages ? null : $paged + 1,
        'prev_page' => (int) $paged === 1 ? null : $paged - 1,
    ];

    在这里,我们使用了 wp_count_posts 函数来计算已发布的项目页面总数。有关此函数的更多信息,请访问 https://developer.wordpress.org/reference/functions/wp_count_posts/。

  8. 登录 WordPress 管理后台,转到 “工具” 下的 “固定链接”,然后单击 “保存更改” 按钮以刷新 WordPress 固定链接规则。

  9. 转到您的浏览器并测试您刚刚创建的自定义 API 路由:

    /wp-json/api/v1/menu/
    /wp-json/api/v1/page/&lt;slug>/
    /wp-json/api/v1/projects/&lt;number>/
    /wp-json/api/v1/project/&lt;slug>

您应该在浏览器屏幕上看到一堆 JSON 原始数据。JSON 原始数据可能难以阅读,但您可以使用 JSONLint(一个 JSON 验证器)在 https://jsonlint.com/ 上漂亮地打印您的数据。或者,您也可以直接使用 Firefox,它具有漂亮打印数据的功能。

您可以在本书 GitHub 仓库的 /chapter-18/crossdomain/backend/wordpress/ 中找到此功能的完整代码。您还可以在其中找到一个示例数据库 (nuxt-wordpress.sql)。此示例数据库中用于登录 WordPress 管理后台的默认用户名和密码是 admin。

做得好!您已成功扩展了 WordPress REST API,使其支持自定义文章类型。我们不需要在 WordPress 中开发任何新的主题来查看我们的内容,因为这将由 Nuxt 处理。我们可以保留 WordPress 现有的主题来预览内容。这意味着我们仅使用 WordPress 来远程托管我们的网站内容,包括所有媒体文件(图像、视频等)。此外,我们可以使用 Nuxt 生成静态页面(就像我们在前几章中所做的那样),并将所有媒体文件从 WordPress 流式传输到我们的 Nuxt 项目,以便我们可以在本地托管它们。我们将在下一节中学习如何做到这一点。

与 Nuxt 集成并从 WordPress 流式传输图像

将 Nuxt 与 WordPress REST API 集成类似于你之前章节中学习和创建的跨域 API 的集成方式。然而,在本节中,我们将改进用于加载图像的插件,使其从 /assets/ 目录请求图像。但是,由于我们的图像上传到 WordPress CMS 并保存在我们 WordPress 项目的 /uploads/ 目录中,我们需要重构我们的资源加载器插件,以便当在 /assets/ 目录中找到图像时,它从该目录请求图像;否则,我们只需从 WordPress 远程加载它们。让我们开始吧:

  1. 在 Nuxt 配置文件中为 Axios 实例设置远程 URL,如下所示:

    // nuxt.config.js
    const protocol = 'http'
    const host = process.env.NODE_ENV === 'production' ? 'yourdomain.com' : 'localhost'
    const ports = {
      local: '3000',
      remote: '4000'
    }
    const remoteUrl = protocol + '://' + host + ':' + ports.remote
    module.exports = {
      env: {
        remoteUrl: remoteUrl,
      }
    }
  2. 创建一个 Axios 实例并直接将其注入到 Nuxt 上下文作为 $axios。此外,使用 inject 函数将此 Axios 实例添加到上下文中的 app 选项中:

    // plugins/axios.js
    import axios from 'axios'
    let baseURL = process.env.remoteUrl
    const api = axios.create({ baseURL })
    export default (ctx, inject) => {
      ctx.$axios = api
      inject('axios', api)
    }
  3. 重构资源加载器插件,如下所示:

    // plugins/utils.js
    import Vue from 'vue'
    Vue.prototype.$loadAssetImage = src => {
      var array = src.split('/')
      var last = [...array].pop()
      if (process.server && process.env.streamRemoteResource === true) {
        var { streamResource } = require('~/assets/js/stream-resource')
        streamResource(src, last)
        return
      }
      try {
        return require('~/assets/images/' + last)
      } catch (e) {
        return src
      }
    }

    在这里,我们将图像 URL 字符串分割成一个数组,从数组的最后一个元素获取图像的文件名(例如,my-image.jpg),并将其存储在 last 变量中。然后,我们使用文件名 (last) 在本地请求图像。如果抛出错误,则表示该图像在 /assets/ 目录中不存在,因此我们直接返回图像的 URL (src)。

    好的,我们将在服务器端应用运行时,当 streamRemoteResource 选项为 true 时,使用 streamResource 函数将远程 URL 的图片流式传输到 /assets/ 目录。你将在接下来的步骤中了解如何创建此选项(就像 remoteURL 选项一样)。

  4. 在 /assets/ 目录下创建一个 stream-resource.js 文件,其中包含 streamResource 函数,如下所示:

    // assets/js/stream-resource.js
    import axios from 'axios'
    import fs from 'fs'
    
    export const streamResource = async (src, last) => {
      const file = fs.createWriteStream('./assets/images/' + last)
      const { data } = await axios({
        url: src,
        method: 'GET',
        responseType: 'stream'
      })
      data.pipe(file)
    }

    在这个函数中,我们使用普通的 Axios 通过指定 stream 作为响应类型来请求远程资源的数据。然后,我们使用 Node.js 内置文件系统(fs)包中的 createWriteStream 函数以及必要的文件路径,在 /assets/ 目录下创建图像。

  5. 在 Nuxt 配置文件中注册这两个插件:

    // nuxt.config.js
    plugins: [
      '~/plugins/axios.js',
      '~/plugins/utils.js',
    ],
  6. 重构 /pages/ 目录下首页的 index.vue 文件,以便使用这两个插件,如下所示:

    // pages/index.vue
    <script>
    export default {
      async asyncData ({ error, $axios }) {
        let { data } = await $axios.get('/wp-json/api/v1/page/home')
        return { post: data }
      }
    }
    </script>
    
    <template v-for="slide in post.slides" :key="slide.id">
      <img :src="$loadAssetImage(slide.image.sizes.medium_large)">
    </template>

    在这里,我们使用了插件中的 $axios 来请求 WordPress API。接收到数据后,我们将其填充到 <template> 块中。$loadAssetImage 函数用于运行加载和处理图像的逻辑。

    /pages/ 目录下的其余页面(/about.vue、/contact.vue、/projects/index.vue、/projects/_slug.vue 和 /projects/pages/_number.vue)以及 /components/ 目录下的组件 /projects/projectitems.vue 都应该按照我们在首页遵循的相同模式进行重构。你可以在本节末尾提供的 GitHub 存储库中找到这些已完成文件的存储库路径。

  7. 在 Nuxt 项目的 package.json 文件中创建另一个带有自定义环境变量 NUXT_ENV_GEN 的脚本命令,并将其值设置为 stream:

    // package.json
    "scripts": {
      "generate": "nuxt generate",
      "stream": "NUXT_ENV_GEN=stream nuxt generate"
    }

    在 Nuxt 中,如果在 package.json 文件中创建一个以 NUXT_ENV_ 为前缀的环境变量,它将自动注入到 Node.js 进程环境中。之后,你可以在整个应用程序中通过 process.env 对象(包括你在 Nuxt 配置文件的 env 属性中设置的其他自定义属性)访问它。有关 Nuxt 中 env 属性的更多信息,请访问 https://nuxtjs.org/api/configuration-env/。

  8. 在 Nuxt 配置文件的 env 属性中为资产加载器插件(我们在步骤 3 中重构了它)定义 streamRemoteResource 选项,如下所示:

    // nuxt.config.js
    env: {
      streamRemoteResource: process.env.NUXT_ENV_GEN === 'stream' ? true : false
    },

    当 NUXT_ENV_GEN 环境变量的值为 stream 时,streamRemoteResource 选项将被设置为 true;否则,它始终设置为 false。因此,当此选项设置为 true 时,资产加载器插件将开始为我们流式传输远程资源到 /assets/ 目录。

  9. (可选) 如果 Nuxt 爬虫由于某些未知原因未能检测到动态路由,则在 Nuxt 配置文件的 generate 选项中手动生成这些路由,如下所示:

    // nuxt.config.js
    import axios from 'axios'
    
    export default {
      generate: {
        routes: async function () {
          const projects = await axios.get(remoteUrl + '/wp-json/api/v1/projects')
          const routesProjects = projects.data.map((project) => {
            return { route: '/projects/' + project.post_name, payload: project }
          })
    
          let totalMaxPages = Math.ceil(routesProjects.length / 6)
          let pagesProjects = []
          Array(totalMaxPages).fill().map((item, index) => {
            pagesProjects.push({ route: '/projects/pages/' + (index + 1), payload: null })
          })
    
          const routes = [ ...routesProjects, ...pagesProjects ]
          return routes
        }
      }
    }

    在这个可选步骤中,我们使用 Axios 获取了属于 projects 文章类型的所有子页面,并使用 JavaScript 的 map 方法循环这些页面以生成它们的路由。然后,我们获取了子页面的长度,并通过将子页面数量除以六(每页显示六个项目)来计算出最大页数 (totalMaxPages)。之后,我们使用 JavaScript 的 Array 对象将 totalMaxPages 转换为数组,然后使用 JavaScript 的 fill、map 和 push 方法循环该数组,以便为分页生成动态路由。最后,我们使用 JavaScript 的展开运算符将来自子页面和分页的路由连接起来,并将它们作为单个数组返回,供 Nuxt 生成动态路由。

  10. 在终端中先运行 stream 命令,然后再运行 generate 命令,如下所示:

    $ npm run stream && npm run generate

    我们使用 stream 命令通过生成第一批静态页面将远程资源流式传输到 /assets/ 目录,然后使用 generate 命令重新生成静态页面。此时,webpack 将处理 /assets/ 目录中的图像,并将它们与静态页面一起导出到 /dist/ 文件夹中。因此,在运行这两个命令后,你应该看到远程资源已在 /assets/ 和 /dist/ 中流式传输和处理。你可以导航到这两个目录并检查下载的资源。

    你可以在本书 GitHub 存储库的 /chapter-18/crossdomain/frontend/nuxt-universal/nuxt-wordpress/axios-vanilla/ 中找到本节的 Nuxt 应用程序。

做得好!你已成功将 Nuxt 与 WordPress REST API 集成,并为静态页面流式传输了远程资源。WordPress 可能不是每个人的选择,因为它不符合 PHP 标准建议 (PSRs) (https://www.php-fig.org/) 并且有其自己的一套做事方式。但它早在 PSR 和许多现代 PHP 框架之前(2003 年)就发布了。从那时起,它一直能够支持无数的企业和个人。当然,它也在不断发展,并为编辑和开发人员提供了最用户友好的管理 UI 之一。

如果这还没有说服你使用 WordPress 作为 API,还有其他选择。在下一节中,我们将研究 REST API 的替代方案——GraphQL API,以及 Node.js 中 WordPress 的替代方案——Keystone。Keystone 使用 GraphQL 来提供其 API。在深入研究 GraphQL 之前,我们将了解 Keystone 并学习如何开发自定义 CMS。