与WordPress REST API交互

虽然 WP REST API 通常用于从 WordPress 获取数据,但它也可以用来执行其他操作。

REST API 还允许你创建、更新和删除各种 WordPress 数据类型。

在本课中,你将了解 WP REST API 模式、验证 WP REST API 请求的方法、测试 WP REST API 请求的工具,以及通过 WP REST API 添加、编辑或删除数据的几种方式。

如果你跳过了本模块前面的课程,请从仓库自述文件中的链接下载 Bookstore 插件 1.0.1 版本,并在本地 WordPress 安装中安装并激活该插件。

WP REST API 模式

在使用 REST API 时,最好将 WP REST API 文档中的端点参考部分放在手边。端点参考列出了 WordPress 核心附带的所有端点。

点击某个端点,例如文章,将显示该端点的模式。模式定义了在获取或创建特定类型数据时,该资源存在的所有字段。

如果你创建了自定义文章类型,比如 bookstore 插件中的书籍自定义文章类型,那么自定义文章类型端点的模式将与文章端点类似。

你会注意到,许多端点字段与 WordPress 数据库中与该数据类型相关的表中的字段相匹配。然而,有些字段略有不同。例如,文章端点的标题字段将与文章表中的 post_title 字段对应。记住这些差异很重要,并且在通过 API 交互时使用正确的字段名称。

身份验证

默认情况下,WordPress REST API 使用与登录 WordPress 仪表盘时相同的基于 Cookie 的身份验证方法。

对于任何非公开的 REST API 端点,或者需要验证用户才能查看或修改的端点,必须存在身份验证 Cookie。

例如,块编辑器就是这样工作的。

有多种验证请求的方法,包括 JSON Web 令牌和 OAuth。

WordPress 内置的另一种方法是应用程序密码。

应用程序密码可以按用户设置,用于验证对 WP REST API 的请求。这允许你让用户访问 API,而无需共享他们用于登录 WordPress 仪表盘的密码。

要为你的用户创建应用程序密码,请导航到用户列表中的该用户,点击该用户进行编辑。滚动到屏幕底部,在应用程序密码部分下。

为新的应用程序密码命名,然后点击添加新应用程序密码。

密码将为你生成。请务必复制并安全地存储它,因为你将无法再次看到它。

在此屏幕上,如果密码泄露,你也可以撤销它。

使用应用程序密码为你的用户测试 REST API 请求,是使用 REST API 测试工具的好方法。

如果你打算构建更复杂的东西,比如连接到 WordPress REST API 的移动应用,你应该考虑使用 JSON Web 令牌或 OAuth 1.0a。

Postman

有许多工具可用于测试 REST API 请求。

例如,如果你使用 PhpStorm,它内置了 HTTP 客户端;如果你使用 VS Code,则有像 Postcode 这样的扩展。还有像 Hoppscotch 和 Postman 这样的独立工具。你甚至可以在终端中使用 curl 命令测试 REST API 端点。

为了本课的目的,你将学习如何使用 Postman 测试一些 REST API 请求。

你可以从 Postman 网站下载 Postman。默认情况下,Postman 会创建一个初始工作区,用于存储你的请求集合。

安装后,打开 Postman,点击创建集合按钮。这将创建一个新集合,你可以在其中添加多个请求进行测试。

你可以为集合命名,以区别于其他集合。在集合内部,点击添加请求按钮。

这将打开一个新请求,你可以为请求指定一个唯一名称。然后,输入本地 books 端点的 URL,并点击发送按钮。

http://yourlocal.wordpress.site/wp-json/wp/v2/books

请求将被发送,JSON 响应将被解析并显示在响应区域中。

现在,创建一个新请求,输入相同的 books 端点 URL,但这次将请求方法更改为 POST,然后点击发送。通过将请求方法更改为 POST,你告诉服务器你想要创建或可能更新一本书。

这次你将看到一个错误消息,因为你没有经过身份验证。

要验证请求,请点击“授权”选项卡,然后从下拉菜单中选择“基本认证”。

接着,输入你的用户名以及之前创建的应用程序密码,然后点击保存按钮。

这次你不会遇到同样的错误,因为你已经通过了身份验证。现在你可以创建书籍了。

继续操作,点击请求中的“正文”选项卡,并选择“原始”单选按钮。然后,从下拉菜单中选择 JSON,并输入以下 JSON 内容:

{
    "title": "My Postman Book",
    "content": "This is my Postman book",
    "status": "publish"
}

再次点击发送,书籍将被创建,并返回新书籍的 JSON 响应。

为了确认,请前往 WordPress 后台检查书籍列表,你应该能看到这本书。

要更新一本书,你可以使用与添加书籍相同的请求配置,但需要修改端点 URL,使其包含书籍 ID。

要删除一本书,你可以使用与更新书籍相同的端点 URL,但需要将请求方法改为 DELETE,并且不在请求正文中发送任何数据。

你还会注意到,删除一篇文章实际上会将其移至回收站,而不是永久删除。这与 WordPress 后台的行为一致。

使用 Postman 这样的工具来测试 REST API 端点,是学习如何使用 WP REST API 的好方法。它对于测试 WP REST API 请求也非常有用,可以确保你打算发送的数据格式正确,并且请求被发送到了正确的端点。

创建一本书

让我们使用 WP REST API 和 api-fetch 来创建一本新书。

为此,我们需要将标题和内容字段作为 POST 请求发送到 books 端点。

你已经有一个可以列出书籍的插件,所以可以以此为基础开始。

首先,你需要更新页面,添加一个表单,用于输入要创建的书籍的标题和内容。你可以使用以下 HTML 来创建表单,并将其添加到 bookstore_render_booklist() 管理页面回调函数中,放在现有 HTML 代码下方:

<div style="width:50%;">
    <h2>Add Book</h2>
    <form>
        <div>
            <label for="bookstore-book-title">Book Title</label>
            <input type="text" id="bookstore-book-title" placeholder="Title">
        </div>
        <div>
            <label for="bookstore-book-content">Book Content</label>
            <textarea id="bookstore-book-content" cols="100" rows="10"></textarea>
        </div>
        <div>
            <input type="button" id="bookstore-submit-book" value="Add">
        </div>
    </form>
</div>

这段 HTML 代码在自定义管理页面中添加了一个新表单,允许你输入新书的标题和内容。表单还包含一个提交按钮。

添加表单后,下一步是添加 JavaScript 代码,用于处理按钮点击事件:

const submitBookButton = document.getElementById( 'bookstore-submit-book' );
if ( submitBookButton ) {
    submitBookButton.addEventListener( 'click', function () {
        // create post code
    } );
}

现在你已经添加了按钮点击事件监听器,可以添加处理书籍创建的代码了。

为此,建议创建一个单独的函数来创建书籍,并在点击事件中调用该函数。

首先,你需要创建一个 submitBook 函数:

function submitBook() {
    // create book code
}

然后更新点击事件监听器,使其调用该函数:

submitBookButton.addEventListener( 'click', submitBook );

submitBook 函数内部,你需要从表单字段中获取标题和内容的值:

    const title = document.getElementById( 'bookstore-book-title' ).value;
    const content = document.getElementById( 'bookstore-book-content' ).value;

现在,你可以使用 api-fetch 向 books 端点发送请求,将路径设置为 books 端点,请求方法设置为 POST,并将 titlecontent 作为数据对象传递:

    wp.apiFetch( {
        path: '/wp/v2/books/',
        method: 'POST',
        data: {
            title: title,
            content: content
        },
    } ).then( ( result ) => {
        alert( 'Book saved!' );
    } );

打开自定义管理页面,输入标题和内容,然后点击“添加”按钮。你应该会看到一个提示,显示“书籍已保存!”。

然后,如果你浏览到书籍列表,就会看到新书被列出来了。

更新和删除书籍

你也可以使用 WP REST API 来更新和删除书籍。

你可以使用与添加项目相同的 api-fetch 实现来更新项目。你需要更新路径,使其包含要更新的数据实体(本例中为书籍)的 ID,以便更新该项目,同时更新数据对象,为要更新的字段提供新值。

    wp.apiFetch( {
        path: '/wp/v2/books/' + id,
        method: 'POST',
        data: {
            title: newTitle,
            content: newContent
        },
    } ).then( ( result ) => {
        alert( 'Book Updated!' );
    } );

删除一篇文章只需要将路径设置为该项目的 URL,并将方法设置为 DELETE

    wp.apiFetch( {
        path: '/wp/v2/books/' + id,
        method: 'DELETE',
    } ).then( ( result ) => {
        alert( 'Book deleted!' );
    } );

进一步阅读

有关使用 WP REST API 创建、更新和删除数据的更多信息,请查看 WP REST API 手册中的“使用 REST API”部分,以及区块编辑器手册中的 api-fetch 包参考。

使用WordPress REST API

WordPress REST API 提供了一个统一的接口,用于与 WordPress 站点中的数据进行交互。

在本课中,你将学习如何使用 WP REST API 从你的 WordPress 站点获取数据。

你将发现三种进行 REST API 请求的内部选项,然后使用它们执行 GET 请求来获取一些公开的自定义文章类型数据。

Bookstore 插件

如果你完成了“WordPress 插件入门”模块,你应该已经构建了一个注册名为 book 的自定义文章类型的插件。

如果你跳过了那个模块,你可以通过点击 Bookstore 插件链接,从 GitHub 仓库下载主插件代码。

安装并激活插件后,在你的代码编辑器中打开主插件文件。

你会注意到传递给 register_post_type 函数的参数之一是 show_in_rest

register_post_type( 'book',
    array(
        'labels'      => array(
            'name'          => __( 'Books', 'textdomain' ),
            'singular_name' => __( 'Book', 'textdomain' ),
        ),
        'public'      => true,
        'has_archive' => true,
        'show_in_rest' => true,
    )
);

此参数设置为 true,这意味着该自定义文章类型在 REST API 中可用。

这意味着如果你浏览到 wp-json/wp/v2/book 路由,你将在响应中看到自定义文章类型的数据。

如果你查看 register_post_type 函数参考,你会发现一些额外的参数可用于控制 REST API 响应。

例如,你可以更改 rest_base 参数来改变自定义文章类型数据可用的路由。

考虑到你期望能够从 book 路由获取多本书,将 rest_base 改为 books 是个好主意。

register_post_type( 'book',
    array(
        'labels'      => array(
            'name'          => __( 'Books', 'textdomain' ),
            'singular_name' => __( 'Book', 'textdomain' ),
        ),
        'public'      => true,
        'has_archive' => true,
        'show_in_rest' => true,
        'rest_base'   => 'books',
    )
);

这样做将允许你向 wp-json/wp/v2/books 路由发出请求,通过 REST API 访问书籍。

发起 REST API 请求

假设你想在 WordPress 仪表盘中添加一个页面,该页面获取书籍并以逗号分隔的列表形式显示书名和永久链接。

首先,你可以使用 admin_menu 钩子和 add_submenu_page 函数,在“书籍”菜单下添加一个管理子菜单页面。

add_action( 'admin_menu', 'bookstore_add_admin_menu' );
function bookstore_add_admin_menu() {
    add_submenu_page(
        'edit.php?post_type=book',
        __( 'Book List', 'textdomain' ),
        __( 'Book List', 'textdomain' ),
        'manage_options',
        'book-list',
        'bookstore_render_booklist'
    );
}

然后,你需要创建 bookstore_render_booklist 回调函数,该函数将输出管理页面的 HTML。

function bookstore_render_booklist() {
    ?>
    <div class="wrap">
        <h1>Book List</h1>
        <button id="load-books" class="button button-primary">Load Books</button>
        <textarea id="book-list" rows="10" cols="50"></textarea>
    </div>
    <?php
}

如果你浏览到仪表盘并点击“书籍”菜单,你会看到一个名为“Book List”的新子菜单页面。

点击该链接将带你进入一个包含“Load Books”按钮和一个文本区域的页面。

现在,你可以向 bookstore_render_booklist 函数添加功能,通过 PHP 获取书籍列表,并使按钮触发页面刷新。

然而,为了获得更流畅的用户体验,你希望使用 JavaScript 和 REST API 异步获取书籍数据并填充书籍列表,而无需等待整个页面刷新。

加载管理 JavaScript

在“WordPress 插件入门”模块中,你学习了如何在插件中加载 JavaScript 文件。

由于此功能被添加到管理仪表盘中,你需要设置一个单独的 wp_enqueue_script 函数调用,并将其挂载到 admin_enqueue_scripts 钩子上,这样 JavaScript 文件就只在管理仪表盘中加载。

首先,在插件目录中创建一个新的 JavaScript 文件,命名为 admin_bookstore.js

然后,将以下代码添加到主插件文件中,以在仪表盘中加载该 JavaScript 文件:

add_action( 'admin_enqueue_scripts', 'bookstore_enqueue_admin_scripts' );
function bookstore_enqueue_admin_scripts() {
    wp_enqueue_script(
        'bookstore-admin',
        plugin_dir_url( __FILE__ ) . 'admin_bookstore.js',
        array(),
        '1.0.0',
        true
    );
}

请注意,这段代码不仅加载了 JavaScript 文件,还指定了一个空的依赖项数组、一个版本号,并通过将最后一个参数设置为 true,指定它应在 HTML 页面的页脚中加载。

你可以在 wp_enqueue_script 函数参考的“参数”部分阅读有关这些参数的更多信息。

你可以通过在 admin_bookstore.js 文件中添加一个简单的 alert,然后刷新管理页面来测试它是否正确加载。

alert( 'Hello, World!' );

一旦你确认它正常工作,就可以从文件中删除该行。

选项 1:使用 Backbone.js 客户端

自从 REST API 被添加到 WordPress 以来,它就包含了一个 Backbone.js REST API JavaScript 客户端,用于直接向 WP REST API 发出请求。

它通过为所有通过 API 暴露的端点提供模型和集合,提供了一个使用 WP REST API 的接口。

为了确保你的 JavaScript 代码能够使用 REST API 客户端,你需要将其添加为已加载 JavaScript 的依赖项。

wp_enqueue_script 的第三个参数是一个包含所有依赖项的数组,你可以将 wp-api 作为依赖项添加到你的 wp_enqueue_script 函数调用中。

add_action( 'admin_enqueue_scripts', 'bookstore_enqueue_admin_scripts' );
function bookstore_enqueue_admin_scripts() {
    wp_enqueue_script(
        'bookstore-admin',
        plugin_dir_url( __FILE__ ) . 'admin_bookstore.js',
        array( 'wp-api' ),
        '1.0.0',
        true
    );
}

这将确保你的插件 JavaScript 代码仅在 REST API JavaScript 客户端加载完成后才加载,从而你可以在插件中使用它。

你需要先为新按钮注册一个点击事件处理器。

const loadBooksByRestButton = document.getElementById( 'bookstore-load-books' );
if ( loadBooksByRestButton ) {
    loadBooksByRestButton.addEventListener( 'click', function () {
        //do somthing
    } );
}

然后,在事件处理函数中,你可以通过全局对象 wp 访问 WP API 客户端,来创建一个新的书籍集合。

    const allBooks = new wp.api.collections.Books();

此时,allBooks 只是一个空集合,因此你需要通过调用集合的 fetch 方法来获取文章。

   allBooks.fetch();

fetch 方法返回一个 Promise,因此你可以链式调用 done 方法来处理响应,并实现一个回调函数来接收 API 请求的响应。

你可以在该回调函数中指定一个 books 参数,用于接收 API 请求的响应。

    allBooks.fetch().done(
        function ( books ) {
            // do something with books
        }
    );

现在,你可以使用类似 forEach 的方法遍历 books 对象,并逐一访问每本书。

    allBooks.fetch().done(
        function ( books ) {
            books.forEach( function ( book ) {
              // do something with book
            } );
        }
    );

最后,你可以将书名和永久链接添加到文本区域中。

首先,你需要在 forEach 循环之前创建一个文本区域的实例,然后在 forEach 循环内部将值追加到文本区域的 value 属性中。

        allBooks.fetch().done(
            function ( books ) {
                const textarea = document.getElementById( 'bookstore-booklist' );
                books.forEach( function ( book ) {
                    textarea.value += book.title.rendered + ',' + book.link + ',\n'
                });
            }
        );

你的最终代码将类似这样。

const loadBooksByRestButton = document.getElementById( 'bookstore-load-books' );
if ( loadBooksByRestButton ) {
    loadBooksByRestButton.addEventListener( 'click', function () {
        const allBooks = new wp.api.collections.Books();
        allBooks.fetch().done(
            function ( books ) {
                const textarea = document.getElementById( 'bookstore-booklist' );
                books.forEach( function ( book ) {
                    textarea.value += book.title.rendered + ',' + book.link + ',\n'
                });
            }
        );
    });
}

切换回自定义的“书籍列表”管理页面,点击“加载书籍”按钮,即可看到书籍列表出现在文本区域中。

选项 2:使用 @wordpress/fetch-api

自 WordPress 5.0 引入区块编辑器以来,@wordpress/fetch-api 包也已可用,用于发起 REST API 请求。

该包是浏览器 fetch API 的封装,提供了一种更现代、更灵活的方式来向 REST API 发起请求。

要使用 fetch API,你可以更新插件的 JavaScript 依赖项,加入 wp-api-fetch

    wp_enqueue_script(
        'bookstyle-script',
        plugins_url() . '/bookstore/admin_bookstore.js',
        array( 'wp-api', 'wp-api-fetch' ),
        '1.0.0',
        true
    );

你可以移除 wp-api 依赖,或者将 wp-api-fetch 作为额外依赖添加。

接下来,在表单的“操作”区域添加一个按钮,用于触发 fetch 请求。

<button id="bookstore-fetch-books">Fetch Books</button>

和之前一样,在你的 bookstore.js 文件中,为新按钮的点击事件设置一个事件监听器,但这次使用 apiFetch 方法来向 REST API 发起请求。

const fetchBooksByRestButton = document.getElementById( 'bookstore-fetch-books' );
if ( fetchBooksByRestButton ) {
    fetchBooksByRestButton.addEventListener( 'click', function () {
        wp.apiFetch( { path: '/wp/v2/books' } ).then( ( books ) => {
            const textarea = document.getElementById( 'bookstore-booklist' );
            books.map( ( book ) => {
                textarea.value += book.title.rendered + ',' + book.link + ',\n'
            });
        } );
    });
}

注意,你将指向书籍端点的路径作为一个对象传递给 wp.apiFetch 函数。这比使用 Backbone.js 客户端更灵活,因为后者需要使用特定的集合来访问书籍。

你可以链式调用 then 方法来处理响应。这与 Backbone 示例中使用 done 方法类似,因为它返回一个 Promise,等待 REST API 请求完成,然后将结果返回给回调函数。

在回调函数内部,你可以访问 books 对象,并使用 map 方法遍历它,将书名和永久链接追加到文本区域。

你还会注意到,这段代码使用了箭头函数语法作为接收响应的回调,这是一种更现代的 JavaScript 函数编写方式。

刷新管理页面,点击“获取书籍”按钮,即可看到书籍列表出现在文本区域中。

选项 3:使用 @wordpress/core-data

如果你正在开发区块,还有一个 core-data 包可用,用于从 REST API 访问数据。

core-data 旨在简化对核心 WordPress 实体的访问和操作。它注册了自己的存储,并提供了许多选择器,可自动从 WordPress REST API 解析数据,同时提供派发动作创建器来操作数据。

core-data 使用了大量 React 功能,因此最适合在区块上下文中使用。

让我们看看如何在区块中使用 core-data 模块从 REST API 获取书籍。

首先,使用你在“区块开发入门”模块中学到的 create-block 工具,创建一个名为 bookstore-block 的新区块。

cd path/to/local/site/wp-content/plugins
npx @wordpress/create-block bookstore-block

这将搭建新区块的脚手架,并生成一些代码供你编辑。

在区块的 edit.js 文件中,从 @wordpress/data 包导入 useSelect 钩子,以及从 @wordpress/core-data 包导入 store

import { useSelect } from '@wordpress/data';
import { store as bookStore } from '@wordpress/core-data';

然后,你可以使用这些来从 REST API 获取书籍。

    const books = useSelect(
        select =>
            select( bookStore ).getEntityRecords( 'postType', 'book' ),
        []
    );

useSelect 是一个钩子,允许你从已注册的选择器中检索数据。

useSelect 接受一个回调函数作为第一个参数,在该函数中你可以使用 bookStore 存储的 getEntityRecords 选择器从 REST API 中检索书籍。这些书籍随后会被存储在 books 变量中。

最后,你可以更新代码,使其在没有返回书籍时返回一个空组件,或者遍历书籍对象并输出书名和链接。

    if ( ! books ) {
        return (
            <div { ...useBlockProps() }></div>
        )
    }

    return (
        <div { ...useBlockProps() }>
            { books.map( ( book ) => (
                <p>
                    <a href={ book.link }>{book.title.rendered}</a>
                </p>
            ) ) }
        </div>
    );

现在,运行区块构建步骤,激活插件,并将书店区块添加到文章或页面中。

你将看到区块输出从 REST API 获取的书名和链接。

不同选项之间的区别

Backbone 客户端是三个选项中最古老的,但它也是与 REST API 集成最紧密的。如果你需要使用 WP REST API 构建管理仪表板页面,它是一个不错的选择,并且远优于使用传统的 admin-ajax.php 端点。

apiFetch 是一个出色的全能解决方案,因为它既可用于管理仪表板页面,也可用于编辑器中的区块。它也是一种更现代的向 REST API 发起请求的方式,并且比 Backbone 客户端更灵活。

core-data 最适合在区块上下文中使用,因为它使用了在区块编辑器上下文之外不可用的 React 功能。

进一步阅读

有关在 WordPress 中使用 REST API 的各种方式的更多信息,请查看 REST API 手册中的 Backbone JavaScript 客户端部分、区块编辑器手册中的 api-fetch 包,以及区块编辑器手册中的 core-data 包。

WordPress REST API

当您为 WordPress 进行开发时,有许多 API 可用于与您的站点数据进行交互。其中最重要的 API 之一就是 REST API。

本课程作为 WordPress REST API 的入门介绍。

您将了解什么是 REST API,以及一些关键的 REST API 概念,例如路由、端点和全局参数,这些将通过您可以在浏览器中执行的一系列示例请求来学习。

您还将了解在哪里可以找到关于 WP REST API 的更多信息。

什么是 WordPress REST API?

WordPress REST API 提供了一个接口,供应用程序与 WordPress 站点进行交互。这些应用程序可以是 WordPress 插件、主题,或需要访问 WordPress 站点数据的自定义应用程序。

WordPress REST API 最著名的实现之一是块编辑器,它是一个通过 REST API 与 WordPress 数据进行交互的 JavaScript 应用程序。

如果您打开浏览器的开发者工具并查看“网络”选项卡,您可以看到当您与块编辑器交互时,向 WordPress REST API 发出的请求。

REST API 是什么意思?

API 代表应用程序编程接口。它是一组允许应用程序相互交互的功能。WordPress 有许多 API,REST API 只是其中之一。

REST 代表表述性状态转移,这是一种软件架构风格,描述了物理上独立的组件之间的统一接口。

其核心是,WordPress REST API 提供了代表文章、页面、分类法以及任何其他自定义数据类型的 REST 端点(URI)。您的代码可以以 JavaScript 对象表示法(即 JSON)的形式向这些端点发送和接收数据,以获取、修改和创建您站点上的内容。

让我们深入了解 REST API 的一些概念,以便更好地理解它们。

路由与端点

WordPress REST API 的上下文中,路由是一个可以映射到不同 HTTP 方法的 URI。

HTTP 方法是您与网络上任何内容交互时发出的请求类型。例如,当您浏览到网络上的一个 URL 时,会向服务器发出一个 GET 请求以请求数据。

当您提交表单时,会发出一个 POST 请求,该请求将提交的表单数据传递给 Web 服务器。

将单个 HTTP 方法映射到路由的过程称为端点。

因此,例如,您会有一个用于获取数据的 GET 端点、一个用于创建数据的 POST 端点以及一个用于删除数据的 DELETE 端点,所有这些都使用相同的路由。

本地开发测试

关于在本地 WordPress 安装上测试 REST API 路由,需要注意的一点是,您可能需要启用除“朴素”之外的固定链接设置。

这是因为 REST API 使用与固定链接相同的 URL 重写功能,将人类可读的路由和端点映射到相关的内部请求。

因此,如果您的本地 WordPress 安装使用的是默认的“朴素”固定链接设置,请将其更改为类似“文章名”的设置。

示例路由与端点

让我们看一些路由和端点的示例。

如果您打开浏览器,并访问 WordPress 站点的 /wp-json/ URI,您将向该 URI 发出一个 GET 请求,该请求会返回一个 JSON 响应。

// 示例代码块,保持原样

某些浏览器内置支持“美化打印”JSON 响应,这将以更易读的格式显示它。

如果您使用 Firefox 查看 JSON 响应,它允许您在不同视图之间切换,以及检查请求头。

根据您的需求,也有浏览器扩展程序,例如 Chrome 的 JSON Formatter 或 Safari 的 JSON Peep。

返回的数据是一个 JSON 响应,显示了哪些路由可用,以及每个路由内有哪些端点可用。

在此示例中,/wp-json/ 是一个路由,当该路由收到 GET 请求时,由显示数据的端点处理。此数据就是所谓的 WordPress REST API 的索引。

相比之下,/wp-json/wp/v2/posts 路由提供了一个返回文章列表的 GET 端点,但也提供了一个 POST 端点。如果您是经过身份验证的用户,并且通过 POST 请求向 /wp-json/wp/v2/posts 路由提交了正确的数据,则该请求由创建新文章的端点处理。

通常,同一个路由(在本例中为 /wp-json/wp/v2/posts)将针对不同的 HTTP 方法有不同的端点,包括用于获取数据的 GET、用于创建数据的 POST 和用于删除数据的 DELETE。

全局参数

WP REST API 包含许多全局参数,这些参数控制 API 如何处理请求/响应处理。这些参数在实际资源之上的层运行,并且对所有资源都可用。

全局参数作为查询字符串参数在 REST API 路由上实现。查询字符串以 ? 开头,后跟一系列由 & 分隔的 key=value 键值对。

看看你之前查看过的 /wp-json/wp/v2/posts 路由,通过在浏览器中请求该路由,从而激活 GET 端点。如你所见,默认情况下会返回文章的所有可用字段。

不过,你可以通过添加 _fields 全局参数来更新路由,然后以逗号分隔的列表形式指定你希望在响应中返回的字段。

wp-json/wp/v2/posts?_fields=author,id,excerpt,title,link

如果你通过刷新浏览器发起第二个 GET 请求,那么只有你请求在响应中返回的字段才会可用。

分页与排序

WP REST API 还支持结果的分页和排序。

分页由 per_pagepageoffset 参数处理。

例如,你可以通过向路由添加 per_page 参数,将 wp-json/wp/v2/posts 路由更新为每页仅返回 5 篇文章。

wp-json/wp/v2/posts?_fields=author,id,excerpt,title,link&per_page=5

如果你通过刷新页面发起新的 GET 请求,则只会返回前 5 篇文章。

还可以使用 orderorder_by 参数对结果进行排序。

例如,你可以将 wp-json/wp/v2/posts 路由更新为按文章标题降序排列。

wp-json/wp/v2/posts?_fields=author,id,excerpt,title,link&per_page=5&orderby=title&order=asc

延伸阅读

WordPress 开发者资源网站有一个专门介绍 REST API 的完整章节,其中包含关于 REST API 关键概念、常见问题、使用和扩展 REST API 等内容的部分。

静态块与动态块

在开发 WordPress 区块时,你需要考虑区块的功能,以及它是否需要根据外部因素而变化。

幸运的是,根据你的需求,可以创建静态或动态区块。

让我们看看静态区块和动态区块之间的区别,如何确定哪种适合你的需求,以及不同的开发方法。

静态区块

如果你一直在按照本模块的课程构建版权日期区块,那么你一直在构建一个静态区块。

由于静态区块的内容是固定的,一旦将其添加到编辑器,save 函数被触发,并且文章或页面被保存,区块的内容将不会改变。

静态区块适用于不需要更改的内容,比如引用或推荐语。

动态区块

然而,如果你考虑版权日期区块的实际需求,理想情况下,如果物理年份发生变化,区块的渲染内容也应该更新。

否则,你需要编辑任何添加了该区块的地方,以触发 save 函数并更新年份。

这就是动态区块的用武之地。

动态区块不通过 save 函数渲染其内容,而是使用 PHP 在包含该区块的文章或页面收到前端请求时渲染其内容。

让我们看看将版权日期区块转变为动态区块需要做些什么。

使版权日期区块动态化

要使版权日期区块动态化,你需要指定一个包含区块渲染逻辑的 PHP 文件或函数。

这可以通过几种方式完成,但最简单的是在 block.json 文件的区块元数据中使用 render 属性。

打开 src 目录中的 block.json 文件,并将以下代码添加到该文件的底部:

"render": "file:./render.php"

这告诉 WordPress 使用名为 render.php 的文件来在前端渲染区块的内容。

然后,你可以在 src 目录中创建一个 render.php 文件,并将渲染逻辑添加到该文件中:

<?php
    $block_props = get_block_wrapper_attributes();
    $starting_year = $attributes['startingYear'];
    $current_year = date( 'Y' );
?>
<p <?php echo $block_props?>>
    Copyright © <?php echo $starting_year?> - <?php echo $current_year; ?>
</p>

你可以使用 get_block_wrapper_attributes 函数来获取区块的包装器属性。这类似于在 JavaScript 中调用 useBlockProps

然后,你可以从 PHP 的 $attributes 数组中获取 startingYear 值。这个 $attributes 变量是暴露给你为区块的 render 元数据属性设置的文件中的三个变量之一,它包含为区块设置的任何属性。

然后,你可以创建 $current_year 变量,它使用 PHP 的 date() 函数始终获取当前年份。这样,当区块被渲染时,它总是获取当前年份。

最后但同样重要的是,输出段落标签,包括所有相关属性和内容。

一旦你设置好了 render.php 文件,你可以删除编辑器中与区块保存过程相关的任何代码,因为你不需要将任何内容保存到文章中。

为此,你可以删除 save.js 文件。

你还可以删除传递给 registerBlockTypesave 属性,以及导入 save 函数的 import 语句。

完成这些更改后,你可以运行构建过程,然后创建一篇文章,并将区块添加到文章中。

你会看到区块在编辑器中仍然按预期渲染。但是,如果你在代码编辑器中查看区块,你会看到保存的版本不包含任何输出。

当你预览它时,内容会被渲染。

然而,如果你模拟年份变化,比如通过更改 PHP 中变量的值,区块的前端渲染将相应更新。

其他资源

有关开发静态区块与动态区块之间差异的更多信息,你可以阅读区块开发基础中的静态或动态渲染区块部分。

区块支持与样式

区块的优势之一,就是能够在每个区块级别控制其外观。

要实现这一点,你可以使用所谓的区块支持功能,并定义区块的样式。

让我们来看看这是如何工作的。

区块支持

区块支持是一种API,允许区块声明对某些常见功能的支持。

例如,大多数区块支持设置对齐方式、背景色、文字颜色、排版等功能。

你可以在区块元数据中定义对这些常见功能的支持,一旦启用,区块在编辑器中就会拥有这些功能。

添加区块支持

要添加对某个功能的支持,你需要在block.json文件的区块元数据的supports属性中定义它。

打开src目录中的block.json文件,更新supports属性,加入align对齐支持。

{
    "supports": {
        "align": true
    }
}

构建过程完成后,创建一篇文章,并将该区块添加到文章中。你会看到该区块现在可以设置对齐方式了。

如你所见,只需启用对齐支持,区块就具备了设置对齐的能力,无需任何额外代码。

启用任何可用的区块支持,只需将它们添加到block.json文件的supports属性中即可,这能为你的用户提供丰富的选项来自定义区块外观。

区块样式

然而,在某些情况下,你可能更愿意自己定义区块的样式。

在“搭建新区块”课程中定义的版权日期区块需求中,我们要求该区块始终具有特定的边框和边框颜色。

这就是style.scsseditor.scss文件的用武之地。这些文件允许你设置特定的区块样式,然后这些样式会在编辑器和前端应用。

这两个文件是语法上很棒的样式表,也称为Sass文件。就像用于创建区块的JSX格式在构建步骤中转换为常规JavaScript一样,Sass文件会转换为常规CSS。这些文件遵循新的SCSS语法,你可以在Sass网站上了解更多信息。幸运的是,你也可以在Sass文件中直接编写纯CSS,这同样有效。

在“搭建新区块”课程中,你了解了这两个文件的用途:

  • style.scss:此文件中的样式会应用于前端和编辑器中的区块
  • editor.scss:此文件中的样式仅应用于编辑器中的区块

如果你需要某个特定样式同时应用于前端和编辑器中的区块,可以将样式添加到style.scss文件中。

打开src目录中的style.scss文件,你会看到以下脚手架代码:

.wp-block-create-block-copyright-date-block {
    background-color: #21759b;
    color: #fff;
    padding: 2px;
}

你可能注意到这个样式并没有应用到当前区块上。这是因为通过useBlockProps应用到区块父容器的类名是根据区块名称自动生成的。

block.json文件中,区块的名称是copyright-date/copyright-date-block,因此生成的类名是wp-block-copyright-date-copyright-date-block

你在style.scss文件中看到的被定位的类名是基于区块的原始名称,所以你需要修改它,使其与生成的类名匹配。

同时,你也可以为区块添加边框和边框颜色。

.wp-block-copyright-date-copyright-date-block {
    background-color: #21759b;
    color: #fff;
    padding: 2px;
    border: 2px solid #000;
}

因为你希望边框始终显示,所以不需要定义任何特定的编辑器样式。这意味着你可以删除editor.scss文件。

你也可以删除block.json文件中的editorStyle属性。

以及edit.js文件中导入editor.scss文件的代码。

这样做可能会破坏你的开发服务器,因此你可能需要重新启动它。

运行构建过程后,创建一篇文章,并将该区块添加到文章中。

你会看到该区块现在具有你在style.scss文件中定义的边框和边框颜色。当你预览区块时,该样式也会在前端应用。

总结

在开发区块时,思考哪些外观元素你希望用户能够编辑,哪些应该始终应用于区块,是很有用的。然后,你可以添加相应的支持,或者将任何特定样式硬编码到相关的样式文件中。

有关区块支持的更多信息,请参阅区块编辑器手册中的详细支持页面。

区块属性

构建块的好处之一是能够允许用户通过块属性来控制块的外观和行为。

让我们学习如何向块添加属性,以及如何向块添加控件,让用户能够更改这些属性。

向块添加属性

属性是块中可由用户控制的特性。例如,对于版权日期块,起始年份就是一个用户可以更改的属性。

要向块添加属性,您需要在块的元数据文件 block.json 中定义它们。

打开 src 目录中的 block.json 文件,并添加以下代码:

{
    "apiVersion": 2,
    "name": "example/copyright-date-block",
    "title": "Copyright Date Block",
    "category": "text",
    "icon": "calendar",
    "description": "显示版权日期",
    "supports": {
        "html": false
    },
    "attributes": {
        "startingYear": {
            "type": "string",
            "default": "2020"
        }
    },
    "editorScript": "file:./index.js",
    "editorStyle": "file:./index.css",
    "style": "file:./style-index.css"
}

JSON 格式的好处之一是,只要使用预期的属性名称,您就可以在现有 JSON 对象中的任何位置添加新属性。在这种情况下,attributes 属性是块注册过程所预期的,因此您可以将其添加到 JSON 对象中的任何位置。

在此示例中,我们向块添加了一个名为 startingYear 的属性。type 属性定义了属性的数据类型,default 属性设置了属性的初始值。

如果您正在运行开发服务器,您可能会注意到在编辑块元数据时它会崩溃。如果发生这种情况,只需重新启动开发服务器即可。

访问块的属性

要在块的编辑组件中访问块的属性,您可以在编辑组件函数中指定一个 props 参数。

编辑组件和保存函数都设置为始终接受这个包含块所有属性的 props 对象。

export default function Edit( props ) {
    // 在这里使用 props
}

然后,您可以使用 props 对象访问块的属性。例如,要访问 startingYear 属性,您可以使用 props.attributes.startingYear

export default function Edit( props ) {
    const blockProps = useBlockProps();
    return (
        <p { ...blockProps }>
            © { props.attributes.startingYear }
        </p>
    );
}

您还可以更新保存函数以包含块的属性。

export default function save( props ) {
    const blockProps = useBlockProps.save();
    return (
        <p { ...blockProps }>
            © { props.attributes.startingYear }
        </p>
    );
}

每次访问起始年份属性时,不必写出 props.attributes.startingYear,您可以使用一种称为解构赋值的语法,先从 props 对象中提取 attributes,然后再从 attributes 中提取 startingYear

export default function Edit( { attributes } ) {
    const blockProps = useBlockProps();
    return (
        <p { ...blockProps }>
            © { attributes.startingYear }
        </p>
    );
}
export default function save( { attributes } ) {
    const blockProps = useBlockProps.save();
    return (
        <p { ...blockProps }>
            © { attributes.startingYear }
        </p>
    );
}

如果您不熟悉对象解构,这是一种从对象中提取属性并将其分配给变量的方法。一开始可能有点奇怪,但一旦习惯了,它就能节省大量时间和代码。

让构建过程完成,然后将块添加到文章或页面中,您会看到块现在显示起始年份属性,并带有您定义的默认值。

块恢复

如果您碰巧在文章或页面中测试块,并在每次更改块属性的默认值或 save 函数时刷新浏览器,有时可能会遇到以下错误:

此块包含意外或无效的内容。

这是因为当块的 save 函数运行时,它会将 save 函数的输出与数据库中已保存的输出进行比较。如果它们不同,就会显示此错误。

如果您打开浏览器开发者工具的“控制台”选项卡,您会看到这被报告为块验证错误。

您可以通过使用“尝试块恢复”按钮来修复此问题,该按钮会重新渲染块并重新保存其输出。

向块添加设置面板

要允许用户更改块的属性,您需要使用块控件。

有两种添加控件的方法:一种是在块工具栏中(当块被选中时出现在块上方),另一种是在设置侧边栏中(也称为检查器,当块被选中时出现在侧边栏中)。

由于 startingYear 属性是一个文本字符串,您可以在块侧边栏中使用 TextControl 来允许用户更改起始年份。

要为您的块向块侧边栏添加控件,您首先需要导入一些内容。

  • 您需要从 @wordpress/block-editor 包中导入 InspectorControls 组件。
  • 您需要从 @wordpress/components 包中导入 PanelBodyTextControl 组件。

首先将这些导入添加到您的 Edit 组件的顶部。

打开 src 目录中的 edit.js 文件,找到导入 useBlockProps 的那一行。

import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
import { PanelBody, TextControl } from '@wordpress/components';

InspectorControls 组件也可以以相同的方式从 @wordpress/block-editor 包中导入。

import { useBlockProps, InspectorControls } from '@wordpress/block-editor';

PanelBodyTextControl 组件可以通过相同的方式从 @wordpress/components 包中导入。

import { PanelBody, TextControl } from '@wordpress/components';

现在你可以使用这些组件为区块侧边栏添加控件。

首先,在 Edit 组件的输出中添加 InspectorControls 组件。该组件是区块侧边栏中控件的包装容器。

然后添加一个 PanelBody 组件,并为其设置 title 属性。

<InspectorControls>
    <PanelBody title={ __( 'Settings', 'copyright-date-block' ) }>
        Settings
    </PanelBody>
</InspectorControls>

在“构建你的第一个区块”课程中,你应该记得 React 组件只能返回一个父级容器。

目前你还没有一个单一的父级容器,因为你在段落组件旁边添加了 InspectorControls 组件。

这也是你的 IDE 可能会提示代码存在问题的原因。

此时,你有两个选择。

你可以更新 Edit 组件,使其渲染一个父级 div 标签,并将区块属性移到父级 div 上。

<div { ...useBlockProps() }>
    <InspectorControls>
        <PanelBody title={ __( 'Settings', 'copyright-date-block' ) }>
            Testing
        </PanelBody>
    </InspectorControls>
    <p>
        { __(
            'Copyright',
            'copyright-date-block'
        ) }
        © { startingYear } - { currentYear }
    </p>
</div>

或者,你可以使用 React Fragment 将所有内容包裹在一个父级容器中。

<>
    <InspectorControls>
        <PanelBody title='Settings'>
            Testing
        </PanelBody>
    </InspectorControls>
    <p { ...useBlockProps() }>
        { __(
            'Copyright',
            'copyright-date-block'
        ) }
        © { startingYear } - { currentYear }
    </p>
</>

由于该区块的功能实际上只需要段落标签,因此在这种情况下使用 Fragment 是最佳选择。如果你的区块需要更多标记,例如段落上方的标题标签,那么使用 div 选项可能更合理。

构建过程完成后,如果你将区块添加到文章或页面,并启用编辑器设置侧边栏,你将看到添加到区块侧边栏的设置面板。

为了遵循 WordPress 插件开发实践,你可能需要进行一个小更新:使用 __() 函数来翻译 PanelBody 组件的标题。

<PanelBody title={ __( 'Settings', 'copyright-date-block' ) }>

向区块侧边栏添加 TextControl

设置面板就位后,你现在可以添加一个 TextControl 组件,让用户编辑你的属性。

TextControl 组件是一个文本输入字段,允许用户输入字符串。你需要在 TextControl 组件上设置三个属性。前两个是标签和值:

  • label:输入字段上方显示的标签
  • value:输入字段的值

让我们看看这会是怎样的效果:

    <PanelBody title={ __( 'Settings', 'copyright-date-block' ) }>
        <TextControl
            label={ __( 'Starting Year', 'copyright-date-block' ) }
            value={ startingYear }
        />
    </PanelBody>

你需要设置的另一个属性是 onChange 属性。该属性是一个函数,当输入字段的值发生变化时被调用,并接收用户输入的新值。

    <PanelBody title={ __( 'Settings', 'copyright-date-block' ) }>
        <TextControl
            label={ __( 'Starting Year', 'copyright-date-block' ) }
            value={ startingYear }
            onChange={ ( newStartingYear ) => {
                // update startingYear with newValue
            } }
        />
    </PanelBody>

然后,该函数用于更新区块的 startingYear 属性。

这个函数的语法与你之前看到的略有不同。这是一个箭头函数语法的示例,它是 JavaScript 中编写函数的一种更简洁的方式。

为了更新属性,你可以使用传递给 Edit 组件的 props 对象上的另一个属性——setAttributes 函数。该函数用于将区块的任何属性更新为新值。

你可以将 setAttributes 添加到从 props 对象解构的属性列表中,然后使用它来更新 startingYear 属性。

export default function Edit( { attributes, setAttributes } ) {

然后,要更新 startingYear 属性,你需要使用 setAttributes 函数,并将 startingYear 属性的新值传递给它。

    <PanelBody title={ __( 'Settings', 'copyright-date-block' ) }>
        <TextControl
            label={ __( 'Starting Year', 'copyright-date-block' ) }
            value={ startingYear }
            onChange={ ( newStartingYear ) => {
                setAttributes( { startingYear: newStartingYear } );
            } }
        />
    </PanelBody>

等待构建过程完成,然后将区块添加到文章或页面。你会看到现在可以在区块侧边栏中更改起始年份的值,并且区块会实时更新。

你还可以将区块编辑器切换到代码编辑器视图,你会看到起始年份值以 JSON 对象的形式存储在区块包装器上。

最后,如果你预览区块,它会使用 startingYear 属性的新值。

其他资源

如需进一步阅读这些主题,请务必查看区块编辑器手册中的属性指南。

区块功能

对插件文件和区块元数据做出任何更改后,就可以开始添加区块的核心功能了。

通常,这包括通过 Edit 组件在编辑器中添加区块的功能,以及通过 save 函数定义区块如何存储其输出内容。

让我们看看添加区块功能需要做些什么。

添加区块的编辑功能

通常,最好先构建区块的 Edit 组件,这样它就能在编辑器中正常运行。

目前,区块在编辑器中显示的是脚手架文本:“Copyright Date Block – hello from the editor!”,以及脚手架样式。

如果你打开 src 目录中的 edit.js 文件,滚动到导入语句下方,你会看到以下代码:

export default function Edit() {
    return (
        <p { ...useBlockProps() }>
            { __(
                'Copyright Date Block – hello from the editor!',
                'copyright-date-block'
            ) }
        </p>
    );
}

这段代码用于在编辑器中显示区块,也称为 Edit 组件。这里需要注意几点。

首先,该组件返回的内容看起来像一个包含文本的段落标签:

return (
        <p { ...useBlockProps() }>
            { __(
                'Copyright Date Block – hello from the editor!',
                'copyright-date-block'
            ) }
        </p>
    );

这段代码被称为 JSX,它是一种特殊的语法,看起来像 HTML,但实际上是 JavaScript。因此,虽然 <p> 标签可能看起来像典型的 HTML 段落标签,但你可以看到标签内部有一些代码,这些代码被包裹在花括号 {} 中。

花括号用于表示其中的代码应被当作 JavaScript 来求值,并将结果插入到 JSX 中。

学习 JSX 的工作原理超出了本课的范围,但你可以在 React 网站上了解更多关于 JSX 的信息。

在这个阶段,重要的是要注意 Edit 组件返回的代码被包裹在一个单一的容器元素中。这是该组件的父容器,任何 React 组件都必须只返回一个单一的父容器。这意味着你不能返回两个 <p> 标签,或者一个 <p> 标签和一个 <div> 标签,除非确保它们都在一个父容器内。

根据你的代码编辑器,如果你这样做,可能会看到各种红色警告。

其次要注意的是 useBlockProps 函数的使用。这是一个特殊的函数,被称为 React 钩子,用于获取区块的属性。

你会看到 useBlockProps 前面有三个点 ...,这在 JavaScript 中被称为展开语法。它获取对象的属性,并将对象的键值对添加到它所应用的对象上。

因为 useBlockProps 返回一个包含属性和值的对象,使用展开语法将这些属性和值作为属性应用到父容器上。

最后,你会注意到这段脚手架代码使用了 WordPress__() 函数。这是一个特殊函数,允许文本被翻译成不同的语言,也称为国际化。

你可以在 WordPress 开发者文档中阅读更多关于国际化的内容。

现在,更新组件,使其返回更符合区块需求的内容。例如:

return (
    <p { ...useBlockProps() }>
        { __(
            'Copyright',
            'copyright-date-block'
        ) }
        © 2019 - 2024
    </p>
);

对于版权符号,你可以使用 &copy; HTML 实体,它会在渲染时被转换为正确的符号。

如果你好奇为什么符号和年份被渲染在 __() 函数之外,那是因为你只需要让“Copyright”这个词可翻译。

你可能不想硬编码结束日期,所以你可以使用一些 JavaScript 来获取当前年份作为变量,并用该变量替换年份。

export default function Edit() {
    const currentYear = new Date().getFullYear().toString();
    return (
        <p { ...useBlockProps() }>
            { __(
                'Copyright',
                'copyright-date-block'
            ) }
            © 2019 - { currentYear }
        </p>
    );
}

完成更改后,保存文件,让构建运行,或者手动运行构建命令。

当你将区块添加到文章或页面时,你应该会看到区块现在显示了你定义的输出内容。

添加区块的保存功能

下一步是更新区块的保存功能,使其在前端正确渲染。

目前,当你预览区块时,它仍然显示脚手架文本。

save.js 文件中的 save 函数是在编辑器中每次保存区块时运行的。这是存储在数据库 post_content 字段中并在前端渲染的内容。

打开 src 目录中的 save.js 文件,你会看到以下代码:

export default function save() {
    return (
        <p { ...useBlockProps.save() }>
            { 'Copyright Date Block – hello from the saved content!' }
        </p>
    );
}

这与 Edit 组件中的脚手架代码非常相似,但有一些不同之处。

一个不同之处是,只有区块属性的特定子集通过 useBlockProps.save() 应用到父容器上。

这是因为 save 函数只关注与前端相关的属性,而不涉及编辑器。

另一个区别是,父标签内的代码全部写在一行上。

这对块的实际功能基本没有影响,只是展示了编写相同代码的不同方式。有些开发者喜欢将代码拆分成多行,因为这样在某些情况下更易读。

因此,你可以更新 save 函数,使其返回与 Edit 组件相同的内容。

export default function save() {
    const currentYear = new Date().getFullYear().toString();
    return (
        <p { ...useBlockProps.save() }>
            { 'Copyright' } © 2019 - { currentYear }
        </p>
    );
}

一旦更新后的块构建完成,将其添加到文章中并预览。你应该会看到块现在显示了保存后的标记。

其他资源

要了解更多关于构建 Edit 和 save 功能的内容,请查阅块编辑器手册中的 Edit 和 Save 参考指南。

构建你的第一个区块

使用 create-block 搭建好区块框架后,你就可以开始调整代码以满足需求了。

让我们看看在上一课中搭建的版权日期区块具体该如何调整。

清理代码

首先,你可以清理掉不需要的框架代码。

导航到 copyright-date-block/src 目录,删除 view.js 文件。同时,从 block.json 文件中移除 viewScript 属性。请确保 block.json 文件中最后一个属性末尾不要留下多余的逗号。

主插件文件

现在打开插件根目录下的 copyright-date-block.php 文件。

这个文件需要修改的地方不多,可能只需要调整插件头部中的 @package 注释。它默认是 create-block,你可以改成更符合插件的名称。

为了本课的目的,我们将其改为 copyright-date

你还可以改进注册区块的代码,将钩子注册移到 init 钩子的回调函数上方,并简化回调函数名称。

<?php
/**
 * Plugin Name:       Copyright Date Block
 * Description:       Example block scaffolded with Create Block tool.
 * Requires at least: 6.6
 * Requires PHP:      7.2
 * Version:           0.1.0
 * License:           GPL-2.0-or-later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:       copyright-date-block
 *
 * @package           copyright-date
 */

/**
 * Registers the block using the metadata loaded from the `block.json` file.
 */
function copyright_date_block_init() {
    register_block_type( __DIR__ . '/build' );
}
add_action( 'init', 'copyright_date_block_init' );

这样代码会更容易阅读和理解。

如你所见,代码使用 register_block_type 函数注册区块,该函数会利用 block.json 文件中的元数据。

因此,现在是检查 block.json 文件的好时机。

区块元数据

block.json 文件以 JSON 格式包含区块的元数据。

{
    "$schema": "https://schemas.wp.org/trunk/block.json",
    "apiVersion": 3,
    "name": "create-block/copyright-date-block",
    "version": "0.1.0",
    "title": "Copyright Date Block",
    "category": "widgets",
    "icon": "smiley",
    "description": "Example block scaffolded with Create Block tool.",
    "example": {},
    "supports": {
        "html": false
    },
    "textdomain": "copyright-date-block",
    "editorScript": "file:./index.js",
    "editorStyle": "file:./index.css",
    "style": "file:./style-index.css",
    "viewScript": "file:./view.js"
}

JSON 代表 JavaScript 对象表示法,它是一种轻量级的数据格式,易于人类读写,也易于机器解析和生成。

JSON 由键值对组成,每个值也可以是嵌套的 JSON 对象。

要修改框架生成的区块元数据以适应你的区块,至少需要更改以下属性的值:

  • 更新 name。这里可以将 create-block 替换为与插件头部中 @package 值相同的名称,即 copyright-date
  • 更新 icon。现在将 icon 的值改为 calendar。这个图标来自 Gutenberg 图标库。
  • 更新 description,使其更符合你的区块。

你的 block.json 文件应该类似这样:

{
    "$schema": "https://schemas.wp.org/trunk/block.json",
    "apiVersion": 3,
    "name": "copyright-date/copyright-date-block",
    "version": "0.1.0",
    "title": "Copyright Date Block",
    "category": "widgets",
    "icon": "calendar",
    "description": "A block that displays the copyright date.",
    "example": {},
    "supports": {
        "html": false
    },
    "textdomain": "copyright-date-block",
    "editorScript": "file:./index.js",
    "editorStyle": "file:./index.css",
    "style": "file:./style-index.css"
}

区块的主 JavaScript 文件

src 目录中的 index.js 文件是区块的主 JavaScript 文件。通常你不需要对这个文件做太多修改,因为它已经设置好用于在区块编辑器中注册区块。

在文件顶部,你会看到以下代码行:

import { registerBlockType } from '@wordpress/blocks';

JavaScript 的 import 声明用于从其他地方导入功能(变量、函数、对象)。

这里,代码从 @wordpress/blocks 包中导入了 registerBlockType 函数,这个包是驱动区块编辑器的核心包。

接下来,它导入了 style.scss 文件,该文件包含区块的样式。

然后它导入了 Edit 组件。这个组件是从 edit.js 文件中导出的。

之后,它导入了 save 函数。这个函数是从 save.js 文件中导出的。

最后但同样重要的是,它将 block.json 文件中的 JSON 对象作为 metadata 变量导入。

最后,它使用 registerBlockType 函数注册区块,并传入两个变量:区块名称和一个包含区块属性的配置对象。

在属性对象内部,edit 属性被设置为 Edit 组件的值,save 属性被设置为 save 函数的值。由于 save 属性和导入的 save 函数同名,因此使用了简写语法来设置属性值。

首次构建

现在你已经更新了一些区块代码,可以首次构建你的区块了。

要构建区块,请打开终端并导航到区块插件目录的根目录。然后运行以下命令:

npm run build

这将扫描 src 目录中的内容,并将该目录中的文件编译到 build 目录中。

如果 build 目录不存在,则会自动创建。

每当您对区块代码进行更改时,都需要再次运行此命令来更新构建目录。

另外,还有一个 npm run start 命令,它会启动一个开发服务器,监视 src 目录中的文件更改,并自动将它们构建到 build 目录中。

npm run start

当您正在积极开发区块时,这个功能非常有用。

无论您使用哪种方式,如果打开 WordPress 仪表盘,创建一篇新文章,并添加您的区块,您应该会在区块插入器中看到该区块。

您会注意到图标已经改变,描述也更具体地针对您的区块。

其他资源

您可以在区块编辑器手册的“block.json 中的元数据”部分了解更多关于区块元数据字段的信息,以及在“开发平台”和“使用 JavaScript 处理区块编辑器”页面中了解开发平台和构建流程的更多内容。

搭建新区块

当你为区块开发安装了必要的工具后,就可以开始开发你的第一个区块了。

在本课中,你将了解一个名为 create-block 的工具,它能让你快速搭建第一个区块插件。你将学习为什么要使用 create-block、如何使用它,并审查它生成的代码。

什么是 create-block?

与任何软件开发项目一样,为 WordPress 开发区块需要以特定方式设置一系列文件和文件夹。

有多种方法可以做到这一点,但你理想的做法是遵循某些最佳实践来构建区块。

Create-block 是一个命令行工具,可帮助你按照这些最佳实践搭建新的区块插件。

在软件开发中,脚手架搭建是指为项目创建基本结构的过程,以便你在此基础上进行构建。

使用 create-block,你可以运行一个终端命令来创建新的区块插件,create-block 会按照区块开发的最佳实践为你设置必要的文件和文件夹。

为什么用插件?

虽然不要求必须将区块作为插件的一部分来开发,但这通常是推荐的做法。

主要原因之一是,提交到 WordPress.org 主题目录的主题中不允许包含自定义区块。

然而,如果你是为自己或客户开发主题,则可以在主题中包含自定义区块,区别在于注册区块的方式不同。

但归根结底,区块开发体验和工具设计为与插件配合使用效果最佳,因此本系列课程将遵循这一方式。

我们要构建什么?

在本系列课程中,你将构建一个“版权日期区块”。

这是一个基础但实用的区块,具有以下功能:

  • 显示文本“Copyright”,后跟版权符号(©),然后是起始年份和当前年份
  • 用户应能调整区块在页面上的对齐方式
  • 它应始终应用以下 CSS:border: 1px solid #111111; padding: 5px;
  • 用户还应能调整起始年份

使用 Create Block

要搭建你的第一个区块,请在本地计算机上打开终端,并切换到本地 WordPress 安装目录的 /wp-content/plugins 目录。

通常通过运行以下命令来完成:

cd /path/to/your/wordpress/wp-content/plugins

然后,运行 create-block 命令,并包含一个插件别名。别名是你想创建的插件的唯一名称。

npx @wordpress/create-block copyright-date-block

如果系统要求确认安装 create-block 包,请输入 y 并按回车键。

这将在 copyright-date-block 目录中创建一个新插件,安装所有必需的包,并创建必要的文件。

一旦新插件搭建完成,你的 WordPress 安装目录中应该会出现一个新的插件文件夹,其名称与你定义的别名一致。

如果你浏览到 WordPress 仪表盘并导航到插件页面,应该会看到你的新插件列在那里。

激活它,一旦激活,创建一篇新文章。

你应该能够在编辑器中添加你新搭建的区块。

搭建好的插件

通过在代码编辑器中打开 copyright-date-block 目录,查看搭建好的区块插件的样子。

首先,有三个目录:

  • build 目录是区块代码最终可部署构建版本所在的位置。这是在编辑器中使用区块时驱动区块的代码。你通常不需要触碰此目录或其任何文件。
  • node_modules 目录是所有 Node.js 包所在的位置。这些也称为项目依赖项,你仅在本地区块开发时需要它们。
  • src 目录是你编写区块代码时花费大部分时间的地方。此目录包含你将用于开发区块的文件。这些文件最终会被编译到 build 目录中的代码中。

在这三个目录之后,还有一些文件。

.editorconfig 文件用于统一不同编辑器和 IDE 的编码风格。

.gitignore 由 Git 版本控制系统使用,用于管理哪些代码提交到版本控制。

这些文件的作用超出了区块开发所需的知识范围,所以如果你不知道它们是做什么用的,暂时可以放心忽略它们。

如果你看不到这两个文件,可能需要在目录浏览器或代码编辑器中启用“显示隐藏文件”功能。

接下来是 copyright-date-block.php 文件。这是启动插件执行的主插件文件。

在该文件中,你会看到标准的插件头部信息以及用于注册区块的 PHP 代码。

这里,copyright_date_block_copyright_date_block_block_init 函数调用了 register_block_type() 函数,并将前面提到的 build 目录的路径作为参数传入。

这个函数随后被挂载到 init 动作上。

接下来,你会看到 package-lock.json 文件和 package.json 文件。

package.json 文件是 npm 在开发 JavaScript 项目时使用的文件。

{
    "name": "copyright-date-block",
    "version": "1.0.0",
    "description": "Example block scaffolded with Create Block tool.",
    "main": "index.js",
    "scripts": {
        "build": "wp-scripts build",
        "format": "wp-scripts format",
        "lint:css": "wp-scripts lint-style",
        "lint:js": "wp-scripts lint-js",
        "packages-update": "wp-scripts packages-update",
        "plugin-zip": "wp-scripts plugin-zip",
        "start": "wp-scripts start"
    },
    "devDependencies": {
        "@wordpress/scripts": "^27.9.0"
    }
}

它包含了项目的关键信息,包括可以在项目上运行的任何脚本以及所有依赖项。

依赖项是你的项目正常运行所需的外部包或模块。就区块开发而言,你需要的依赖项是 @wordpress/scripts 包的开发依赖项。这些依赖项会被安装到你的 node_modules 目录中。

scripts 对象包含了一系列可以在开发过程中运行的命令行脚本。其中最重要的有:

  • build:将 src 目录中的文件编译到 build 目录中
  • start:启动一个开发服务器,监视 src 目录中文件的变化,并自动将它们编译到 build 目录中

当你开始开发区块时,会学习如何使用这些脚本。

package-lock.json 文件包含了所有已安装依赖项的列表,以及所使用的依赖项的版本号。它将所需的依赖项锁定到此文件中指定的版本号。这是另一个你现在可以忽略的文件。

最后,还有一个 readme.txt 文件,只有当你打算将插件发布到 WordPress 插件目录时,才需要编辑它。

src 目录

你所有的区块开发工作都在 src 目录中进行。让我们看看生成了哪些文件:

  • block.json 以 JSON 对象的形式存储区块的元数据。你可以在区块元数据手册页面中了解更多关于区块元数据的信息。此文件允许你定义区块名称、标题、图标、组成区块的各种文件等等。
  • edit.js 是你在开发区块时花费大部分时间的地方。此文件导出一个 React Edit() 组件,该组件在编辑器中渲染,并决定区块在编辑器中的外观和功能。
  • editor.scss 包含控制区块在区块编辑器中外观的样式。通常,你会希望区块在编辑器中的外观与网站前端一致,因此你通常根本不需要此文件。
  • index.js 是区块 JavaScript 执行的起点。它设置并执行 registerBlockType 函数,以在编辑器中注册区块。
  • save.js 导出一个 save() 函数,该函数决定在保存文章或页面时将保存到 wp_posts 表中 post_content 字段的标记,因此也决定了区块在前端的外观和功能。
  • style.scss 包含控制区块在编辑器和前端外观的样式。如果你需要区块在编辑器中显示不同,此处的样式可以被 editor.scss 中的样式覆盖。
  • view.js 是一个用于在前端为区块添加任何额外 JavaScript 的文件。这是另一个你通常不需要的文件。

build 目录

在开发过程中,你将执行之前在 package.json 文件中看到的脚本,将 src 目录中的文件编译到 build 目录中。

构建区块代码的过程,也称为打包代码,是将区块代码转换为所有浏览器兼容格式的过程。

当你生成区块脚手架时,create-block 工具也运行了构建过程,为你生成了 build 目录。

你会注意到,有些文件(如 block.json 文件)会原样打包到 build 目录中,而其他文件(如 index.js 文件)则会被转换。还有一些额外的文件,如 index.asset.php 文件,是在构建过程中生成的。

当 WordPress 在编辑器或前端加载你的区块时,它执行的是来自 build 目录中的代码。

package.json 文件中定义为开发依赖的 @wordpress/scripts 包,使用一个名为 Webpack 的工具来打包你的区块代码。

关于其工作原理的细节超出了本课程的范围,但你可以在 Webpack 文档中了解更多信息。

附加资源

要了解更多关于 create-block 工具及其提供的所有不同选项,请查看区块编辑器手册中关于 create-block 的包参考文档。

搭建区块开发环境

WordPress 区块是 WordPress 网站存储和展示内容的默认方式。

让我们快速了解一下什么是区块、它们如何工作,以及开始开发它们需要什么。

WordPress 区块

区块在创建和编辑内容时用于文章和页面编辑器,以及在创建和编辑主题模板或模式时用于站点编辑器。

在底层,区块由一种特定格式的 HTML 注释组合而成,该注释定义了区块,如果需要,还会使用 HTML 实体来表示区块内容。

区块的结构

让我们看一个文章中区块的示例。

在你的本地 WordPress 安装中,创建一个新文章,给它一个标题,并在文章编辑器中输入一些文本。

然后,点击文章右上角的选项图标,选择代码视图。

<!-- wp:paragraph -->
<p>Hello World</p>
<!-- /wp:paragraph -->

如你所见,HTML 段落标签被包裹在名为 wp:paragraph 的 HTML 注释中。这些 wp:paragraph 注释是 WordPress 识别这是一个段落区块的方式。区块的实际内容是 wp:paragraph 标签内的所有内容,在这个例子中,就是 HTML 段落标签及其内部的内容。

现在点击“退出代码编辑器”,在侧边栏中,为区块应用一个背景颜色。

现在切换回代码视图,注意 wp:paragraph 标签包含了一些额外的数据。

<!-- wp:paragraph {"backgroundColor":"luminous-vivid-amber"} -->
<p class="has-background has-luminous-vivid-amber-background-color">Hello World</p>
<!-- /wp:paragraph -->

backgroundColor 属性以一种称为 JSON 的特殊格式添加到区块包装器中。当这篇文章在前端渲染时,WordPress 会将其转换为一个 CSS 类,应用到区块上。

准备工作

除了你的本地 WordPress 安装和代码编辑器之外,你还需要一些额外的工具来开发区块。

你需要一个终端来运行命令。

并且你需要安装 Node.js 和 npm。

关于终端的一切

你首先需要的是访问终端来运行命令。

终端是一种允许你使用文本命令与计算机交互的工具。它也被称为命令行或命令提示符。你的操作系统将决定终端的外观以及你可以使用哪些命令。

在 macOS 上,默认的终端称为 Terminal,位于你的应用程序 -> 实用工具文件夹中。你也可以通过点击 Launchpad 应用并搜索 Terminal 来启动它。

在大多数 Linux 发行版上,默认的终端也称为 Terminal,通常位于应用程序菜单中。

在 Windows 上,默认的终端称为命令提示符,位于开始菜单中。

然而,我们推荐在 Windows 上使用名为 PowerShell 的终端应用程序,因为可以配置 PowerShell 使其工作方式类似于 MacOS 和 Linux 上的终端。

某些 Windows 版本确实预装了 PowerShell,但我们推荐安装来自 Microsoft 网站的版本。要下载 PowerShell,请访问 https://learn.microsoft.com/en-gb/powershell/,点击下载 Powershell 按钮,并安装你下载的可执行文件。

安装完成后,你可以通过在开始菜单中搜索 PowerShell 来启动它。

一旦你有了一个可用的终端,你将能够安装开始开发区块所需的软件。

Node.js 和 npm

区块开发依赖于一个名为 React 的 JavaScript 框架。要使用 React,你需要在本地计算机上安装 Node.js 和 npm。

安装 Node.js

由于 npm 与 Node.js 捆绑在一起,你只需要安装 Node.js 即可开始使用。

虽然有多种方法可以安装带有 npm 的 Node.js,但我们推荐使用一个名为 nvm 的工具,它代表 Node 版本管理器。

你可以在 github.com/nvm-sh 找到关于 nvm 的详细信息。

这将使你能够根据所使用软件的要求,安装和使用不同版本的 Node.js。

在 MacOS 和 Linux 上安装 NVM

如果你使用 MacOS 或 Linux,你可以打开默认的终端应用程序,并通过运行 nvm 安装脚本来安装 nvm,你可以从 nvm 文档中复制该脚本。

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash

安装完成后,你可以使用 nvm install 命令来安装你需要的 Node.js 和 npm 版本。

在 Windows 上安装 NVM

如果你使用的是 Windows 机器,你可以通过 Windows 的 Chocolatey 包管理器(特别是 Chocolatey CLI)来安装 nvm。

首先,右键点击 Powershell 菜单项,选择“以管理员身份运行”,以管理员权限打开 Powershell。

浏览到 Chocolatey CLI 安装文档,向下滚动到使用 PowerShell 安装说明部分。

复制这些说明,然后在 Powershell 窗口中右键点击以粘贴它们。

Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))

按回车键运行该命令。

一旦 Chocolatey CLI(也称为 choco)安装完成,使用以下命令安装 nvm。

choco install -y nvm

nvm 安装完成后,使用 nvm install 命令安装所需的 Node.js 和 npm 版本。

NVM 的使用

在录制本教程时,Node.js 当前的稳定 LTS(长期支持)版本是版本 20,但请查看 Node.js 网站,确认在您安装 Node.js 时是否有更新的版本。

如果当前 LTS 版本高于 20,您可以在以下命令中替换为该版本号。

要安装 Node.js 和 npm,请使用 nvm install 命令,并指定您要安装的版本号。

nvm install 20

也可以通过运行以下命令来安装最新的 LTS 版本。

nvm install --lts

然后,您可以运行 nvm list 来查看已安装的 Node.js 版本。

nvm list

由于 nvm 允许您运行多个版本的 Node.js,您需要告诉 nvm 您要使用哪个版本。可以通过运行 nvm use 命令,后跟版本号来实现。

nvm use 20

这会将当前终端实例的 Node.js 版本设置为版本 20。

此外,还可以使用 LTS 选项运行 npm use 命令。

nvm use --lts

最后,如果您确实安装了多个版本的 Node.js 和 npm,可以通过运行 nvm alias default 命令来设置默认使用的版本。

nvm alias default 20

然后,通过运行以下命令检查已启用的 Node.js 和 npm 版本。

node -v
npm -v

如果您正在处理多个需要不同 Node.js 和 npm 版本的项目,这将非常有用。

现在您已拥有所有必需的工具,可以开始您的区块开发之旅了。

其他资源

WordPress 开发者文档中有一个专门介绍区块编辑器的部分,其中包含大量关于区块、区块开发以及区块开发者可用的各种包的信息。

阅读“区块开发基础”部分也是一个好主意,以便更好地理解整个过程。