在 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 插件:
-
Advanced Custom Fields (ACF) 用于创建自定义元框。有关此插件的更多信息,请访问 https://www.advancedcustomfields.com/
-
ACF Repeater Field 用于创建可重复的子字段集。它是一个 ACF 高级附加组件 (https://www.advancedcustomfields.com/add-ons/)。你可以从 https://www.advancedcustomfields.com/add-ons/repeater-field/ 购买。或者,你可以从 https://www.advancedcustomfields.com/pro/ 默认获取 ACF PRO。
-
Rewrite Rules Inspector 用于检查和刷新 WordPress 中的重写规则。有关此插件的更多信息,请访问 https://www.google.com/search?q=https://wordpress.org/plugins/rewrite-rules-inspector/
如果你不想使用这些插件,可以创建自己的插件和元框。请查看如何在 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:
-
解压下载的 WordPress .zip 文件并从目录安装
-
使用 WordPress CLI (https://make.wordpress.org/cli/handbook/ 或 https://wp-cli.org/)
-
使用 Apache 设置端口(这可能有点麻烦)
-
使用内置的 PHP 服务器
本书将使用内置的 PHP 服务器,因为这是启动 WordPress 最简单的方法,并且如果我们需要在将来移动它,只要它在同一个端口(例如 localhost:4000)上运行,也会更容易。所以,让我们看看如何做到这一点:
-
创建一个目录(也要使其可写),并在其中下载并解压 WordPress。你可以从 https://wordpress.org/ 下载 WordPress。你应该在解压后的 WordPress 目录中看到一些 .php 文件以及 /wp-admin/、/wp-content/ 和 /wp-includes/ 目录。
-
通过 PHP Adminer 创建一个 MySQL 数据库(例如,nuxt-wordpress)。
-
导航到该目录,并使用内置 PHP 运行 WordPress,如下所示:
$ php -S localhost:4000
-
将你的浏览器指向 localhost:4000,并使用所需的 MySQL 凭据(数据库名称、用户名和密码)以及你的 WordPress 用户帐户信息(用户名、密码和电子邮件地址)安装 WordPress。
-
使用你的用户凭据登录 WordPress 管理后台 localhost:4000/wp-admin/,并在“页面”标签下创建一些主要页面(首页、关于、项目、联系方式)。
-
从“外观”下导航到“菜单”,并通过在“菜单名称”输入字段中添加 menu-main 来创建站点导航。
-
选择“添加菜单项”下显示的所有页面(联系方式、关于、项目、首页),然后单击“添加到菜单”将它们添加到 menu-main 作为导航项。你可以拖放和排序这些项目,使它们按以下顺序显示:首页、关于、项目、联系方式。然后,单击“保存菜单”按钮。
-
(可选)在 “设置” 下的 “固定链接” 中,将 WordPress 固定链接从 “朴素” 选项更改为 “自定义结构”(例如,值为 /%postname%/)。
-
下载我们前面提到的插件,并将它们解压到 /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” 主题来扩展我们的子主题,并从那里创建自定义文章类型。让我们开始吧:
-
在 /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/。
-
在 /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/。
-
(可选)我们还可以为这个自定义文章类型添加对分类和标签的支持,如下所示:
'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); }
-
(可选)如果你觉得
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);
-
在
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:
-
创建一个全局命名空间和用于获取导航和单个页面的端点:
// 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/。
-
创建用于获取单个项目页面和列出项目页面的端点:
// 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); });
-
创建一个
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/。
-
创建一个 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/。
-
创建一个 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 对象中。
-
创建一个 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/。
-
我们还需要计算数据(上一页码和下一页码),以便为项目页面制作分页。因此,不要只返回 $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/。
-
登录 WordPress 管理后台,转到 “工具” 下的 “固定链接”,然后单击 “保存更改” 按钮以刷新 WordPress 固定链接规则。
-
转到您的浏览器并测试您刚刚创建的自定义 API 路由:
/wp-json/api/v1/menu/ /wp-json/api/v1/page/<slug>/ /wp-json/api/v1/projects/<number>/ /wp-json/api/v1/project/<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 远程加载它们。让我们开始吧:
-
在 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, } }
-
创建一个 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) }
-
重构资源加载器插件,如下所示:
// 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 选项一样)。
-
在 /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/ 目录下创建图像。
有关 fs 包及其 createWriteStream 函数的更多信息,请访问 https://nodejs.org/api/fs.html 和 https://www.google.com/search?q=https://nodejs.org/api/fs.html%23fs_fs_createwritestream_path_options。有关响应数据中 Node.js 流的 pipe 事件和 Node.js 流本身的更多信息,请访问 https://www.google.com/search?q=https://nodejs.org/api/stream.html%23stream_event_pipe 和 https://www.google.com/search?q=https://nodejs.org/api/stream.html%23stream_stream。
-
在 Nuxt 配置文件中注册这两个插件:
// nuxt.config.js plugins: [ '~/plugins/axios.js', '~/plugins/utils.js', ],
-
重构 /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 存储库中找到这些已完成文件的存储库路径。
-
在 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/。
-
在 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/ 目录。
-
(可选) 如果 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 生成动态路由。
有关 JavaScript 的 map、fill 和 push 方法的更多信息,请分别访问 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map、https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill 和 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push。
-
在终端中先运行 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。