如何在Ubuntu18.04上的WordPress中嵌入React应用程序

来自菜鸟教程
跳转至:导航、​搜索

作为 Write for DOnations 计划的一部分,作者选择了 Electronic Frontier Foundation 来接受捐赠。

介绍

WordPress 是一种流行的内容管理系统,根据 W3Techs(Web 技术调查),它为 Internet 上 33% of 个网站的 提供支持。 它如此受欢迎的一个原因是它很容易设置清晰、直接的文档。 此外,还有大量支持 WordPress 开发人员的社区资源。 WordPress 可以通过廉价甚至免费的开箱即用解决方案解决许多用例。 最后,WordPress 带有一个定义明确的插件系统,它允许开发人员编写自定义代码来添加自己的功能。 这个插件系统有很好的文档,运行良好,并且正如您将在本教程后面看到的那样,很容易使用。

想要提供最丰富、最具交互性体验的开发人员可以使用 JavaScript,由 React 等框架支持。 React 是一个 JavaScript 库,旨在使开发人员能够轻松创建超越典型静态页面或表单的动态、交互式 UI。 由 Facebook 创建,因此在安全性、稳定性和易用性方面得到了很好的维护,React 很受欢迎,因为它具有良好的文档和完善的、社区驱动的文档和插件生态系统。

本教程将引导您了解在 WordPress 站点中嵌入 React 应用程序的最佳实践。 对于它的示例,它将使用一个常见的用例:创建一个小部件,该小部件旨在嵌入多个页面,有时在一个页面上多次嵌入。 在服务器端,它将被实现为 WordPress 短代码 。 简码类似于 HTML 标记,但它使用方括号 ([...]) 而不是尖括号 (<...>)。 它不是直接渲染 HTML 元素,而是调用 PHP 函数,该函数反过来渲染 HTML,插入数据库中的数据。

在本教程结束时,您将创建自己的短代码,将其插入 WP Admin 中的页面,并发布该页面。 在该页面上,您将能够看到浏览器显示的 React 小部件。

先决条件

为了遵循本教程,您必须具备:

  • 使用 Initial Server Setup with Ubuntu 18.04 教程设置的 Ubuntu 18.04 服务器为您的服务器以及具有 root 权限的新用户配置防火墙。
  • 完全注册的域名。 本教程将始终使用 your_domain 作为示例。 您可以在 Namecheap 上购买一个域名,在 Freenom 上免费获得一个域名,或者使用您选择的域名注册商。
  • 为您的服务器设置了以下两个 DNS 记录。 您可以关注这个DigitalOcean DNS的介绍,详细了解如何添加它们。
    • 带有 your_domain 的 A 记录指向您服务器的公共 IP 地址。
    • 带有 www.your_domain 的 A 记录指向您服务器的公共 IP 地址。
  • 在您的服务器上安装 Apache、MySQL 和 PHP。 您可以按照 如何在 Ubuntu 18.04 上安装 Linux、Apache、MySQL、PHP (LAMP) 堆栈来获取此信息。
  • 使用 Let's Encrypt 保护 Apache 遵循 如何在 Ubuntu 18.04 上使用 Let's Encrypt 保护 Apache 以生成免费的 SSL 证书。
  • WordPress 安装,您可以按照 How To Install WordPress with LAMP on Ubuntu 18.04 及其先决条件获得。
  • 按照 如何在 Ubuntu 18.04 上安装 Node.js 中的“使用 PPA 安装”选项安装 Node.js。 本教程将使用 11.15.0 版本,所以当使用 curl 下载安装脚本时,请将 10.x 替换为 11.x 以遵循本文中的步骤教程。

第 1 步 — 更新和配置文件系统权限

当以在 Initial Server Setup with Ubuntu 18.04 先决条件中创建的非 root 用户身份登录时,您将无权查看或编辑 WordPress 目录中的任何文件。 这是一个问题,因为稍后您将添加和修改文件以创建 WordPress 插件和 React 应用程序。 要解决此问题,在此步骤中,您将更新您的 WordPress 配置,以便您有权编辑您的 WordPress 文件。

运行以下命令,将 sammy 替换为您的非 root 用户的名称,并将 /var/www/wordpress 替换为您的 WordPress 目录(这是您在先决条件中创建的 Apache 文档根文件夹)的路径:

sudo chown -R sammy:www-data /var/www/wordpress

让我们分解这个命令:

  • sudo — 这允许您以 root 的身份执行此命令,因为您正在修改 sammy 无权访问的文件。
  • chown - 此命令更改文件所有权。
  • -R - 此标志递归更改所有权,包括所有子文件夹和文件。
  • sammy:www-data - 这将所有者设置为您的非 root 用户 (sammy) 并将组保持为 www-data 以便 Apache 仍然可以访问文件以便为它们提供服务。
  • /var/www/wordpress — 指定 WordPress 目录的路径。 这是所有权将更改的目录。

要验证此命令是否成功,请列出 WordPress 目录的内容:

ls -la /var/www/wordpress

您将看到目录内容的列表:

Outputtotal 216
drwxr-x---  5 sammy www-data  4096 Apr 13 15:42 .
drwxr-xr-x  4 root  root      4096 Apr 13 15:39 ..
-rw-r-----  1 sammy www-data   235 Apr 13 15:54 .htaccess
-rw-r-----  1 sammy www-data   420 Nov 30  2017 index.php
-rw-r-----  1 sammy www-data 19935 Jan  1 20:37 license.txt
-rw-r-----  1 sammy www-data  7425 Jan  9 02:56 readme.html
-rw-r-----  1 sammy www-data  6919 Jan 12 06:41 wp-activate.php
drwxr-x---  9 sammy www-data  4096 Mar 13 00:18 wp-admin
-rw-r-----  1 sammy www-data   369 Nov 30  2017 wp-blog-header.php
-rw-r-----  1 sammy www-data  2283 Jan 21 01:34 wp-comments-post.php
-rw-r-----  1 sammy www-data  2898 Jan  8 04:30 wp-config-sample.php
-rw-r-----  1 sammy www-data  3214 Apr 13 15:42 wp-config.php
drwxr-x---  6 sammy www-data  4096 Apr 13 15:54 wp-content
-rw-r-----  1 sammy www-data  3847 Jan  9 08:37 wp-cron.php
drwxr-x--- 19 sammy www-data 12288 Mar 13 00:18 wp-includes
-rw-r-----  1 sammy www-data  2502 Jan 16 05:29 wp-links-opml.php
-rw-r-----  1 sammy www-data  3306 Nov 30  2017 wp-load.php
-rw-r-----  1 sammy www-data 38883 Jan 12 06:41 wp-login.php
-rw-r-----  1 sammy www-data  8403 Nov 30  2017 wp-mail.php
-rw-r-----  1 sammy www-data 17947 Jan 30 11:01 wp-settings.php
-rw-r-----  1 sammy www-data 31085 Jan 16 16:51 wp-signup.php
-rw-r-----  1 sammy www-data  4764 Nov 30  2017 wp-trackback.php
-rw-r-----  1 sammy www-data  3068 Aug 17  2018 xmlrpc.php

这些文件是您从 wordpress.org 在先决条件 How To Install WordPress with LAMP on Ubuntu 18.04 中下载的名为 latest.tar.gz 的文件中包含在 WordPress 核心中的文件]。 如果权限显示在前面的输出中,这意味着您的文件和目录已正确更新。

在此步骤中,您更新了 WordPress 安装以授予您编辑其文件的权限。 在下一步中,您将使用该访问权限来创建将构成 WordPress 插件的文件。

第 2 步 — 创建一个基本的 WordPress 插件

现在您有权修改 WordPress 目录中的文件,您将创建一个基本的 WordPress 插件并将其添加到安装中。 这将允许 React 在本教程后面与 WordPress 交互。

一个 WordPress 插件可以很简单:

  1. wp-content/plugins 内的目录。
  2. 该目录中具有相同名称和 .php 文件扩展名的文件。
  3. 该文件顶部的特殊注释为 WordPress 提供了重要的插件元数据。

要为稍后编写的 React 代码制作插件,首先要为 WordPress 插件创建一个目录。 为简单起见,本教程将插件命名为 react-wordpress。 运行以下命令,将 wordpress 替换为您的 Apache 文档根目录:

mkdir /var/www/wordpress/wp-content/plugins/react-wordpress

然后,导航到新创建的目录。 后续命令将从这里执行。

cd /var/www/wordpress/wp-content/plugins/react-wordpress

现在让我们创建插件文件。 本教程将使用 nano,通过命令 nano 调用,作为所有文件的命令行文本编辑器。 您也可以自由使用您选择的任何其他文本编辑器,例如 PicoVimEmacs

打开react-wordpress.php进行编辑:

nano react-wordpress.php

将以下行添加到您的文件中以创建插件的开头:

/var/www/wordpress/wp-content/plugins/react-wordpress/react-wordpress.php

<?php
/**
 * @wordpress-plugin
 * Plugin Name:       Embedding React In Wordpress
 */

defined( 'ABSPATH' ) or die( 'Direct script access disallowed.' );

顶部的注释部分提供了插件的元数据,检查 ABSPATH 常量的行可防止不良行为者通过其 URL 直接访问此脚本。 ABSPATH 是你的 WordPress 根目录的绝对路径,所以如果定义了 ABSPATH,你可以确定文件是通过 WordPress 环境加载的。

注意: 插件元数据注释有很多字段可用,但只有 Plugin Name 是必需的。 有关详细信息,请参阅 WordPress 文档中的 标头要求 页面。


接下来,打开网络浏览器并导航到您域的 Plugins 页面 (https://your_domain/wp-admin/plugins.php)。 您将看到您的插件与 WordPress 的默认插件一起列出:

单击 激活 以启用您的插件。

激活插件后,包含插件的行将以蓝色突出显示,左侧带有蓝色边框,而不是下方显示 Activate 的链接,而是显示 [ X217X]停用:

接下来,您将建立插件的结构。

返回终端打开 react-wordpress.php

nano react-wordpress.php

然后更新它以添加以下突出显示的行,这些行定义了有用的常量:

/var/www/wordpress/wp-content/plugins/react-wordpress/react-wordpress.php

<?php
/**
 * @wordpress-plugin
 * Plugin Name:       Embedding React In Wordpress
 */

defined( 'ABSPATH' ) or die( 'Direct script access diallowed.' );

define( 'ERW_WIDGET_PATH', plugin_dir_path( __FILE__ ) . '/widget' );
define( 'ERW_ASSET_MANIFEST', ERW_WIDGET_PATH . '/build/asset-manifest.json' );
define( 'ERW_INCLUDES', plugin_dir_path( __FILE__ ) . '/includes' );

在新添加的行中,您定义了三个常量:

  1. ERW_WIDGET_PATH — 这将是 React 应用程序的路径。
  2. ERW_ASSET_MANIFEST — 这是 React 资产清单的路径,该文件包含 JavaScript 和 CSS 文件列表,您的应用程序需要在页面中包含这些文件才能正常工作。
  3. ERW_INCLUDES — 该子目录将包含所有 PHP 文件。

请注意,每个 define() 指的是 plugin_dir_path( __FILE__ )。 这代表该文件的目录路径。

添加常量定义后,保存文件并退出编辑器。

注意: 命名空间你的常量很重要。 在这种情况下,我们使用命名空间 ERW_,它代表 Embedding React in WordPress。 使用此命名空间为变量添加前缀可确保它们是唯一的,这样它们就不会与其他插件中定义的常量冲突。


要创建包含其他 PHP 文件的 includes/ 文件夹,请从插件目录的顶级 /var/www/your_domain/wp-content/plugins/react-wordpress 开始。 然后,创建文件夹:

mkdir includes

现在您已经构建了制作 WordPress 插件所需的与 PHP 相关的文件和文件夹,您将为 React 创建初始文件和文件夹。

第 3 步 - 初始化 React 应用程序

在这一步中,您将使用 Create React App 来初始化您的 React 应用程序。

本教程使用 Create React App 版本 3.0.1 进行了测试。 版本 3.0.0asset-manifest.json 的结构进行了重大更改,因此此早期版本未经修改与本教程不兼容。 为确保您使用的是此处预期的版本,请运行以下命令来安装 Create React App:

sudo npm install --global create-react-app@3.0.1

此命令将安装 Create React App 的 3.0.1 版本。 --global 标志将在系统范围内安装它。 在系统范围内安装可确保在未指定任何路径的情况下运行 create-react-app(或 npx create-react-app)时,您将使用刚刚安装的版本。

安装 Create React App 后,使用它来创建 React 应用程序。 本教程将应用程序命名为 widget

sudo create-react-app widget

此命令使用 npx,它是 NPM 附带的二进制文件。 它旨在使使用 CLI 工具和其他托管在 NPM 上的可执行文件变得容易。 如果在本地找不到这些工具,它将安装它们。

create-react-app 命令将生成一个项目文件夹和基本 React 应用程序所需的所有文件。 这包括一个 index.html 文件、启动 JavaScript、CSS 和测试文件,以及一个用于定义项目和依赖项的 package.json。 它预先包含依赖项和脚本,让您无需安装和配置任何其他构建工具即可构建应用程序以进行生产。

设置 widget 应用程序后,终端中的输出将如下所示:

Output...
Success! Created widget at /var/www/wordpress/wp-content/plugins/react-wordpress/widget
Inside that directory, you can run several commands:

  npm start
    Starts the development server.

  npm run build
    Bundles the app into static files for production.

  npm test
    Starts the test runner.

  npm run eject
    Removes this tool and copies build dependencies, configuration files
    and scripts into the app directory. If you do this, you can’t go back!

We suggest that you begin by typing:

  cd widget
  npm start

Happy hacking!

接下来,导航到新创建的目录:

cd widget

您现在可以使用 默认构建命令npm run build 构建您的应用程序。 这个 build 命令在 scripts 键下的文件 package.json 中查找名为 build 的脚本:

/var/www/wordpress/wp-content/plugins/react-wordpress/widget/package.json

{
  "name": "widget",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^16.9.0",
    "react-dom": "^16.9.0",
    "react-scripts": "3.1.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

这里调用react-scripts节点模块提供的react-scripts.js可执行文件,是create-react-app提供的核心组件之一。 这反过来调用构建脚本,它使用 webpack 将您的项目文件编译成浏览器可以理解的静态资产文件。 它通过以下方式做到这一点:

  • 解决依赖关系。
  • SASS 文件编译成 CSS 和 JSX 或 TypeScript 编译成 JavaScript。
  • ES6 语法转换为 ES5 语法,具有更好的跨浏览器兼容性。

现在您对 build 有所了解,请在终端中运行以下命令:

sudo npm run build

命令完成后,您将收到类似于以下内容的输出:

Output> widget@0.1.0 build /var/www/wordpress/wp-content/plugins/react-wordpress/widget
> react-scripts build

Creating an optimized production build...
Compiled successfully.

File sizes after gzip:

  36.83 KB (+43 B)  build/static/js/2.6efc73d3.chunk.js
  762 B (+44 B)     build/static/js/runtime~main.a8a9905a.js
  710 B (+38 B)     build/static/js/main.2d1d08c1.chunk.js
  539 B (+44 B)     build/static/css/main.30ddb8d4.chunk.css

The project was built assuming it is hosted at the server root.
You can control this with the homepage field in your package.json.
For example, add this to build it for GitHub Pages:

  "homepage" : "http://myname.github.io/myapp",

The build folder is ready to be deployed.
You may serve it with a static server:

  npm install -g serve
  serve -s build

Find out more about deployment here:

  https://bit.ly/CRA-deploy

您的项目现在已构建,但在进行下一步之前,最佳做法是确保您的应用程序仅在存在时才加载。

React 在 DOM 中使用一个 HTML 元素,在其中呈现应用程序。 这称为 target 元素。 默认情况下,此元素的 ID 为 root。 要确保此 root 节点是您正在创建的应用程序,请更改 src/index.js 以检查命名空间 erw-roottarget 的 ID。 为此,首先打开 src/index.js

sudo nano src/index.js

修改并添加突出显示的行:

/var/www/wordpress/wp-content/plugins/react-wordpress/widget/src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

const target = document.getElementById('erw-root');
if (target) { ReactDOM.render(<App />, target); }

serviceWorker.unregister();

最后,完成编辑后保存并退出文件。

在此文件中,您对默认的 index.js 文件进行了两项重要更改:

  1. 您将目标元素从 <div id="root"></div> 更改为 <div id="erw-root"></div>,以便为您的应用程序命名空间。
  2. 您在 if (...) 语句中包含了对 ReactDOM.render() 的调用,以便仅在应用程序存在时才加载该应用程序。

注意: 如果您希望小部件出现在每个页面上,您可能还希望添加一行错误处理,如果 ID 为 erw-root 的元素,它将向控制台打印一条消息没有找到。 但是,本教程将省略此步骤。 像这样的行会在没有该元素的每个页面上产生控制台错误,包括您不打算在其中包含该元素的页面。 这些多个 JavaScript 控制台错误可能会降低您网站的搜索引擎排名。


更改 src/ 目录中的任何 JavaScript 或 CSS 文件后,重新编译您的应用程序以合并您的更改非常重要。 要重建您的应用程序,请运行:

sudo npm run build

现在,您的 build/ 目录包含了一个以 JavaScript 和 CSS 文件形式工作的 React 应用程序。 下一步涉及设置一些 PHP 文件,这些文件会将您的 JavaScript 和 CSS 排入页面。

第 4 步 — 将 JavaScript 和 CSS 文件排入队列

在此步骤中,您将使用 WordPress 操作和过滤器来:

  1. 在 WordPress 页面加载周期中的适当时间输出脚本入队代码。
  2. 以对页面加载速度影响最小的方式排列您的 JavaScript 和 CSS 文件

WordPress 使用 动作和过滤器 作为它的主要钩子。 操作可以在页面加载周期中的指定时间执行代码,过滤器通过更改您不拥有的函数的返回值来修改特定行为。

要使用这些钩子,您将创建一个 PHP 文件,其中包含解析资产清单的代码。 这是您稍后将用于将所有资产排入队列的同一文件,因此脚本将写入 <head> 标记。

在创建文件之前,使用以下命令导航出包含 React 应用程序的目录并进入顶级 react-wordpress 插件目录:

cd /var/www/wordpress/wp-content/plugins/react-wordpress

includes/ 文件夹中创建 enqueue.php 文件:

nano includes/enqueue.php

首先将打开的 <?php 标签放在文件的顶部。 还要添加检查 ABSPATH 的行,如前所述,这是每个 PHP 文件中的最佳实践:

/var/www/wordpress/wp-content/plugins/react-wordpress/includes/enqueue.php

<?php
// This file enqueues scripts and styles

defined( 'ABSPATH' ) or die( 'Direct script access disallowed.' );

保存并退出此文件。

然后,更新 react-wordpress.php 以要求项目中的 enqueue.php。 首先,打开文件进行编辑:

nano react-wordpress.php

添加以下突出显示的行:

/var/www/wordpress/wp-content/plugins/react-wordpress/react-wordpress.php

<?php
/**
 * @wordpress-plugin
 * Plugin Name:       Embedding React In Wordpress
 */

defined( 'ABSPATH' ) or die( 'Direct script access diallowed.' );

define( 'ERW_WIDGET_PATH', plugin_dir_path( __FILE__ ) . '/widget' );
define( 'ERW_ASSET_MANIFEST', ERW_WIDGET_PATH . '/build/asset-manifest.json' );
define( 'ERW_INCLUDES', plugin_dir_path( __FILE__ ) . '/includes' );

require_once( ERW_INCLUDES . '/enqueue.php' );

WordPress 插件中的一种常见模式是需要 includes/ 目录中的其他 PHP 文件,以便将重要任务拆分成块。 require_once() 函数解析作为参数传递的文件的内容,就好像该文件的 PHP 代码是内联编写的一样。 与类似的命令 include 不同,require 如果找不到您尝试要求的文件,则会引发异常。 使用 require_once()(而不仅仅是 require())确保 enqueue.php 不会在多次给出指令 require_once( ERW_INCLUDES . '/enqueue.php' ); 时被多次解析。

保存并退出文件。

现在重新打开 includes/enqueue.php

nano includes/enqueue.php

然后,添加以下突出显示的代码:

/var/www/wordpress/wp-content/plugins/react-wordpress/includes/enqueue.php

<?php
// This file enqueues scripts and styles

defined( 'ABSPATH' ) or die( 'Direct script access diallowed.' );

add_action( 'init', function() {

  add_filter( 'script_loader_tag', function( $tag, $handle ) {
    if ( ! preg_match( '/^erw-/', $handle ) ) { return $tag; }
    return str_replace( ' src', ' async defer src', $tag );
  }, 10, 2 );

  add_action( 'wp_enqueue_scripts', function() {

  });
});

init 动作添加一个函数意味着此代码将在加载过程的 init 阶段运行,即在您的主题和其他插件加载之后。

使用 script_loader_tag 过滤器在 <script> 标签上设置 asyncdefer 属性告诉浏览器异步加载脚本而不是阻塞 DOM 构建和页面渲染.

然后 wp_enqueue_scripts 操作将前端项目排入队列。 详情见本页

确保写入文件并退出。

您现在已经告诉 WordPress 将脚本和样式表标签写入页面。 在下一步中,您将解析一个名为资产清单的文件。 这将为您提供您需要排队的所有文件的路径。

第五步——解析资产清单

在此步骤中,您会将 React 构建生成的资产清单解析为 JavaScript 和 CSS 文件列表。

当您构建应用程序时,React 构建脚本会将您的项目构建成多个 JavaScript 和 CSS 文件。 文件的数量和名称会因构建而异,因为每个构建都包含文件内容的哈希。 资产清单提供了在最后一次构建中生成的每个文件的名称以及该文件的路径。 通过以编程方式对其进行解析,您可以保证您写入页面的脚本和样式表标签将始终指向正确的文件,即使名称发生更改也是如此。

首先,使用 cat 命令检查 asset-manifest.json

cat widget/build/asset-manifest.json

它看起来像这样:

Output{
  "files": {
    "main.css": "/static/css/main.2cce8147.chunk.css",
    "main.js": "/static/js/main.a284ff71.chunk.js",
    "main.js.map": "/static/js/main.a284ff71.chunk.js.map",
    "runtime~main.js": "/static/js/runtime~main.fa565546.js",
    "runtime~main.js.map": "/static/js/runtime~main.fa565546.js.map",
    "static/js/2.9ca06fd6.chunk.js": "/static/js/2.9ca06fd6.chunk.js",
    "static/js/2.9ca06fd6.chunk.js.map": "/static/js/2.9ca06fd6.chunk.js.map",
    "index.html": "/index.html",
    "precache-manifest.e40c3c7a647ca45e36eb20f8e1a654ee.js": "/precache-manifest.e40c3c7a647ca45e36eb20f8e1a654ee.js",
    "service-worker.js": "/service-worker.js",
    "static/css/main.2cce8147.chunk.css.map": "/static/css/main.2cce8147.chunk.css.map",
    "static/media/logo.svg": "/static/media/logo.5d5d9eef.svg"
  }
}

要解析它,您的代码将查找以 .js.css 结尾的对象键。

打开你的 enqueue.php 文件:

nano includes/enqueue.php

添加突出显示的代码段:

/var/www/wordpress/wp-content/plugins/react-wordpress/includes/enqueue.php

<?php
// This file enqueues scripts and styles

defined( 'ABSPATH' ) or die( 'Direct script access disallowed.' );

add_action( 'init', function() {

  add_filter( 'script_loader_tag', function( $tag, $handle ) {
    if ( ! preg_match( '/^erw-/', $handle ) ) { return $tag; }
    return str_replace( ' src', ' async defer src', $tag );
  }, 10, 2 );

  add_action( 'wp_enqueue_scripts', function() {

    $asset_manifest = json_decode( file_get_contents( ERW_ASSET_MANIFEST ), true )['files'];

    if ( isset( $asset_manifest[ 'main.css' ] ) ) {
      wp_enqueue_style( 'erw', get_site_url() . $asset_manifest[ 'main.css' ] );
    }

    wp_enqueue_script( 'erw-runtime', get_site_url() . $asset_manifest[ 'runtime~main.js' ], array(), null, true );

    wp_enqueue_script( 'erw-main', get_site_url() . $asset_manifest[ 'main.js' ], array('erw-runtime'), null, true );

    foreach ( $asset_manifest as $key => $value ) {
      if ( preg_match( '@static/js/(.*)\.chunk\.js@', $key, $matches ) ) {
        if ( $matches && is_array( $matches ) && count( $matches ) === 2 ) {
          $name = "erw-" . preg_replace( '/[^A-Za-z0-9_]/', '-', $matches[1] );
          wp_enqueue_script( $name, get_site_url() . $value, array( 'erw-main' ), null, true );
        }
      }

      if ( preg_match( '@static/css/(.*)\.chunk\.css@', $key, $matches ) ) {
        if ( $matches && is_array( $matches ) && count( $matches ) == 2 ) {
          $name = "erw-" . preg_replace( '/[^A-Za-z0-9_]/', '-', $matches[1] );
          wp_enqueue_style( $name, get_site_url() . $value, array( 'erw' ), null );
        }
      }
    }

  });
});

完成后,写入并退出文件。

突出显示的代码执行以下操作:

  1. 读取资产清单文件并将其解析为 JSON 文件。 它访问存储在键 'files' 中的内容并将其存储到 $asset_manifest 变量中。
  2. 如果存在,则将主 CSS 文件排入队列。
  3. 首先将 React 运行时排入队列,然后是主 JavaScript 文件,将运行时设置为依赖项以确保它首先加载到页面中。
  4. 解析任何名为 static/js/<hash>.chunk.js 的 JavaScript 文件的资产清单文件列表,并将它们排入主文件之后的页面中。
  5. 解析任何名为 static/css/<hash>.chunk.css 的 CSS 文件的资产清单文件列表,并将它们排入主 CSS 文件之后的页面中。

注意: 使用 wp_enqueue_script()wp_enqueue_style 将导致入队文件的 <script><link> 标签出现在每个页面中。 最后一个参数 true 告诉 WordPress 将文件放在页面内容页脚下方而不是 <head> 元素的底部。 这一点很重要,这样加载 JavaScript 文件就不会减慢页面的其余部分。


在此步骤中,您隔离了应用程序使用的脚本和样式的文件路径。 在下一步中,您将确保这些文件路径指向您的 React 应用程序的 build 目录,并且您的任何源文件都无法从浏览器访问。

第 6 步 — 提供和保护静态文件

此时,您已经告诉 WordPress 要加载哪些 JavaScript 和 CSS 文件以及在哪里可以找到它们。 但是,如果您在浏览器中访问 https://your_domain 并查看 JavaScript 控制台,您将看到 HTTP 404 错误。 (查看 这篇文章 了解更多关于如何使用 JavaScript 控制台的信息。)

这是因为文件的 URL 路由(例如,/static/js/main.2d1d08c1.chunk.js)与文件的实际路径(例如,/wp-content/plugins/react-wordpress/widget/build/static/js/main.2d1d08c1.chunk.js)不匹配。

在这一步中,你将通过告诉 React 构建目录的位置来纠正这个问题。 您还将向 .htaccess 文件添加 Apache 重写规则,以保护您的源文件不被浏览器查看。

要为 React 提供正确的应用程序路径,请在 React 应用程序目录中打开 package.json

sudo nano widget/package.json

然后,添加突出显示的 homepage 行:

/var/www/wordpress/wp-content/plugins/react-wordpress/widget/package.json

{
  "name": "widget",
  "version": "0.1.0",
  "private": true,
  "homepage": "/wp-content/plugins/react-wordpress/widget/build",
  "dependencies": {
    "react": "^16.9.0",
    "react-dom": "^16.9.0",
    "react-scripts": "3.1.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

写入并退出文件。 然后,重建你的 React 应用程序。 移动到 widget/ 的顶层:

cd widget

然后运行 build 命令:

sudo npm run build

构建命令完成后,通过将其内容输出到终端来检查资产清单:

cat build/asset-manifest.json

您将看到文件路径已全部更改:

/var/www/wordpress/wp-content/plugins/react-wordpress/widget/build/asset-manifest.json

{
  "files": {
    "main.css": "/wp-content/plugins/react-wordpress/widget/build/static/css/main.2cce8147.chunk.css",
    "main.js": "/wp-content/plugins/react-wordpress/widget/build/static/js/main.a28d856a.chunk.js",
    "main.js.map": "/wp-content/plugins/react-wordpress/widget/build/static/js/main.a28d856a.chunk.js.map",
    "runtime~main.js": "/wp-content/plugins/react-wordpress/widget/build/static/js/runtime~main.2df87c4b.js",
    "runtime~main.js.map": "/wp-content/plugins/react-wordpress/widget/build/static/js/runtime~main.2df87c4b.js.map",
    "static/js/2.9ca06fd6.chunk.js": "/wp-content/plugins/react-wordpress/widget/build/static/js/2.9ca06fd6.chunk.js",
    "static/js/2.9ca06fd6.chunk.js.map": "/wp-content/plugins/react-wordpress/widget/build/static/js/2.9ca06fd6.chunk.js.map",
    "index.html": "/wp-content/plugins/react-wordpress/widget/build/index.html",
    "precache-manifest.233e0a9875cf4d2df27d6280d12b780d.js": "/wp-content/plugins/react-wordpress/widget/build/precache-manifest.233e0a9875cf4d2df27d6280d12b780d.js",
    "service-worker.js": "/wp-content/plugins/react-wordpress/widget/build/service-worker.js",
    "static/css/main.2cce8147.chunk.css.map": "/wp-content/plugins/react-wordpress/widget/build/static/css/main.2cce8147.chunk.css.map",
    "static/media/logo.svg": "/wp-content/plugins/react-wordpress/widget/build/static/media/logo.5d5d9eef.svg"
  }
}

这会告诉您的应用程序在哪里可以找到正确的文件,但也存在一个问题:它暴露了您应用程序的 src 目录的路径,熟悉 create-react-app 的人可以访问 [ X197X] 并开始探索您的应用程序的源文件。 自己试试吧!

要保护您不希望用户访问的路径,请将 Apache 重写规则添加到 WordPress 的 .htaccess 文件中。

nano /var/www/wordpress/.htaccess

添加四个突出显示的行:

/var/www/wordpress/.htaccess

<IfModule mod_rewrite.c>
RewriteRule ^wp-content/plugins/react-wordpress/widget/(build|public)/(.*) - [L]
RewriteRule ^wp-content/plugins/react-wordpress/widget/* totally-bogus-erw.php [L]
</IfModule>

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

# END WordPress

这告诉 Apache 允许浏览器请求 wp-content/plugins/react-wordpress/widget/build/wp-content/react-wordpress/widget/public/ 上的任何内容。 其他任何内容都将重定向到 totally-bogus-erw.php。 除非您的顶层有一个名为 totally-bogus-erw.php 的文件,否则此请求将由 WordPress 处理,这将呈现 404 错误。

有一些 WordPress 插件,例如 Stream,可以监控请求活动并记录 404。 在日志中,请求将显示用户收到 404 时请求的 IP 地址和页面。 观察 totally-bogus-erw.php 会告诉你一个特定的 IP 地址是否正在尝试爬取你的 React 应用程序的 src 目录。

请务必写入并退出文件。

现在您已经建立了将 JavaScript 和 CSS 文件加载到页面上所需的路由,是时候使用短代码将 HTML 元素添加到 JavaScript 将与之交互以呈现您的应用程序的页面了。

第 7 步 — 创建简码

短代码可以使用非常简单的页内语法插入插入服务器端数据的复杂 HTML 块。 在此步骤中,您将创建并注册一个 WordPress 短代码,并使用它将您的应用程序嵌入到页面中。

导航到插件的顶层:

cd /var/www/wordpress/wp-content/plugins/react-wordpress/

创建一个包含短代码的新 PHP 文件:

touch includes/shortcode.php

然后,编辑您的主 PHP 文件,以便在加载插件时需要 includes/shortcode.php。 先打开react-wordpress.php

nano react-wordpress.php

然后添加以下突出显示的行:

/var/www/wordpress/wp-content/plugins/react-wordpress/react-wordpress.php

<?php
/**
 * @wordpress-plugin
 * Plugin Name:       Embedding React In Wordpress
 */

defined( 'ABSPATH' ) or die( 'Direct script access diallowed.' );

define( 'ERW_WIDGET_PATH', plugin_dir_path( __FILE__ ) . '/widget' );
define( 'ERW_ASSET_MANIFEST', ERW_WIDGET_PATH . '/build/asset-manifest.json' );
define( 'ERW_INCLUDES', plugin_dir_path( __FILE__ ) . '/includes' );

require_once( ERW_INCLUDES . '/enqueue.php' );
require_once( ERW_INCLUDES . '/shortcode.php' );

写入并退出文件。

现在,打开新创建的简码文件:

nano includes/shortcode.php

添加以下代码:

/var/www/wordpress/wp-content/plugins/react-wordpress/includes/shortcode.php

<?php
// This file enqueues a shortcode.

defined( 'ABSPATH' ) or die( 'Direct script access disallowed.' );

add_shortcode( 'erw_widget', function( $atts ) {
  $default_atts = array();
  $args = shortcode_atts( $default_atts, $atts );

  return "<div id='erw-root'></div>";
});

此代码主要包含样板文件。 它注册了一个名为 erw_widget 的短代码,当调用它时,将 React 应用程序的根元素 <div id="erw-root"></div> 打印到页面上。

保存并退出 shortcode.php

要查看 React 应用程序的运行情况,您需要创建一个新的 WordPress 页面并将短代码添加到其中。

在网络浏览器中导航到 https://your_domain/wp-admin。 在页面的最顶部,您会看到一个黑色条,左侧有 WordPress 徽标,然后是房子图标、您的网站名称、评论气泡图标和编号,以及另一个显示 [X203X ]+ 新 。 将鼠标悬停在 + New 按钮上,将下拉菜单。 单击显示 Page 的菜单项。

当屏幕加载时,您的光标将集中在显示 Add title 的文本框中。 单击此处并开始输入以为新页面提供相关标题。 本教程将使用 My React App

假设您使用的是 WordPress Gutenberg 编辑器,您将在页面顶部附近的标题下方看到一行文本,显示为 开始写作或输入 / 选择一个块 . 当您将鼠标悬停在该文本上时,右侧会出现三个符号。 选择最接近的类似于 [/] 添加短代码块:

在新增的文本区域输入简码 [erw_widget]。 然后,点击窗口右上角蓝色的Publish…按钮,再按Publish确认。

您将看到一个绿色条,确认该页面已发布。 点击查看页面链接:

在屏幕上,您将看到您的应用程序:

现在您在页面中有一个基本的 React 应用程序渲染,您可以使用管理员在服务器端提供的选项自定义该应用程序。

第 8 步 — 注入服务器生成的设置

在此步骤中,您将使用服务器生成的数据和用户提供的数据将设置注入应用程序。 这将使您能够在应用程序中显示动态数据并在页面中多次使用小部件。

首先,打开index.js文件:

sudo nano widget/src/index.js

然后,删除 import App from './App'; 行并使用以下突出显示的行更新 index.js 的内容:

/var/www/wordpress/wp-content/plugins/react-wordpress/widget/src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import * as serviceWorker from './serviceWorker';

const App = () => (
  <div className="App">
    <span className="App__Message">Hello,<br />World!</span>
  </div>
);

const target = document.getElementById('erw-root');
if (target) { ReactDOM.render(<App />, target); }

serviceWorker.unregister();

这会修改您的 React 应用程序,因此它不会返回默认的 Create React App 屏幕,而是返回一个读取 Hello, World! 的元素。

保存并退出文件。 然后打开index.css进行编辑:

nano widget/src/index.css

index.css 的内容替换为以下代码:

/var/www/wordpress/wp-content/plugins/react-wordpress/widget/src/index.css

.App {
  width: 100px;
  height: 100px;
  border: 1px solid;
  display: inline-block;
  margin-right: 20px;
  position: relative;
}

.App .App__Message {
  font-size: 15px;
  line-height: 15px;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  text-align: center;
  width: 100%;
}

.App 的样式将呈现一个 100 像素的正方形,带有实线边框,而 .App__Message 的样式将呈现在正方形内垂直和水平居中的文本。

写入并退出文件,然后重建应用程序:

cd widget
sudo npm run build

构建成功后,在浏览器中刷新 https://your_domain/index.php/my-react-app/。 您现在将看到您使用 CSS 设置样式的框,以及文本 Hello, World!

接下来,您将添加自定义设置,包括用户提供的边框颜色和大小。 您还将从服务器传递当前用户的显示名称。

更新简码以接受参数

要传递用户提供的参数,您必须首先为用户提供传递参数的方法。 回到终端,导航回插件的顶层:

cd ..

接下来,打开您的 shortcode.php 文件进行编辑:

nano includes/shortcode.php

更新您的简码文件以包含以下突出显示的行:

<?php
[label /var/www/wordpress/wp-content/plugins/react-wordpress/includes/shortcode.php]
// This file enqueues your shortcode.

defined( 'ABSPATH' ) or die( 'Direct script access disallowed.' );

add_shortcode( 'erw_widget', function( $atts ) {
  $default_atts = array( 'color' => 'black' );
  $args = shortcode_atts( $default_atts, $atts );

  return "<div class='erw-root'></div>";
});

写入并退出文件。 请注意代码如何将 'color' => 'black' 添加到 $default_atts 数组中。 数组键 color 指示 WordPress 期望 color 属性可能被传递给 [erw_widget] 短代码。 数组值 black 设置默认值。 所有简码属性都作为字符串传递给简码函数,因此如果您不想设置默认值,可以使用空字符串 () 代替。 最后一行更改为使用类而不是 ID,因为预计页面中将有多个元素。

现在,返回浏览器并单击 Hello, World! 框下方的 Edit 按钮。 更新浏览器中的 WordPress 页面以添加第二个简码实例,并为这两个实例添加颜色属性。 本教程将使用 [erw_widget color="#cf6f1a"][erw_widget color="#11757e"]

点击蓝色的Update按钮保存。

注意: 第二个小部件还不会显示。 您需要更新 React 应用程序以期望由一个类标识的多个实例,而不是由一个 ID 标识的单个实例。


接下来,打开index.js进行编辑:

sudo nano widget/src/index.js

使用以下内容更新它:

/var/www/wordpress/wp-content/plugins/react-wordpress/widget/src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import * as serviceWorker from './serviceWorker';

const App = () => (
  <div className="App">
    <span className="App__Message">Hello,<br />World!</span>
  </div>
);

const targets = document.querySelectorAll('.erw-root');
Array.prototype.forEach.call(targets, target => ReactDOM.render(<App />, target));

serviceWorker.unregister();

写入并退出文件。 更新后的行将在每个实例上调用具有类 erw-root 的 React 应用程序。 因此,如果短代码被使用两次,页面中将出现两个方块。

最后打开index.css进行编辑:

sudo nano widget/src/index.css

更新文件以包含以下突出显示的行:

/var/www/wordpress/wp-content/plugins/react-wordpress/widget/src/index.css

.erw-root { display: inline-block; }

.App {
  width: 100px;
  height: 100px;
  border: 1px solid;
  display: inline-block;
  margin-right: 20px;
  position: relative;
}

.App .App__Message {
  font-size: 15px;
  line-height: 15px;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  text-align: center;
  width: 100%;
}

使用这条添加的线,多个相邻的小部件将并排显示,而不是一个在另一个之上。

保存并退出文件。

现在,重新编译你的 React 应用程序:

cd widget
sudo npm run build

现在,如果您在浏览器中刷新页面,您将看到两个小部件:

请注意,小部件仍然不显示边框颜色。 这将在以后的部分中解决。

唯一标识每个小部件实例

为了唯一标识每个小部件,需要从服务器传递一个 ID。 这可以通过根元素的 data-id 属性来完成。 这很重要,因为页面上的每个小部件可能有不同的设置。

为此,返回您的顶级插件目录并打开 shortcode.php 进行编辑:

cd ..
nano includes/shortcode.php

更新它以具有以下突出显示的行:

/var/www/wordpress/wp-content/plugins/react-wordpress/includes/shortcode.php

<?php
// This file enqueues your shortcode.

defined( 'ABSPATH' ) or die( 'Direct script access disallowed.' );

add_shortcode( 'erw_widget', function( $atts ) {
  $default_atts = array( 'color' => 'black' );
  $args = shortcode_atts( $default_atts, $atts );
  $uniqid = uniqid('id');

  return "<div class='erw-root' data-id='{$uniqid}'></div>";
});

第一个新行生成一个带有前缀 id 的唯一 ID。 更新后的行使用 data-id 属性将 ID 附加到 React 根。 这将使 ID 在 React 中可访问。

保存文件,但不要退出它。

将设置写入 JavaScript window 对象

在短代码文件中,您将在窗口全局 JavaScript 对象中将设置写入页面。 使用 window 对象确保可以从 React 中访问它。

shortcode.php 仍然打开的情况下,更新它,使其包含以下内容:

/var/www/wordpress/wp-content/plugins/react-wordpress/includes/shortcode.php

<?php
// This file enqueues your shortcode.

defined( 'ABSPATH' ) or die( 'Direct script access disallowed.' );

add_shortcode( 'erw_widget', function( $atts ) {
  $default_atts = array( 'color' => 'black' );
  $args = shortcode_atts( $default_atts, $atts );
  $uniqid = uniqid('id');

  global $current_user;
  $display_name = $current_user ? $current_user->display_name : 'World';

  ob_start(); ?>
  <script>
  window.erwSettings = window.erwSettings || {};
  window.erwSettings["<?= $uniqid ?>"] = {
    'color': '<?= $args["color"] ?>',
    'name': '<?= $display_name ?>',
  }
  </script>
  <div class="erw-root" data-id="<?= $uniqid ?>"></div>

  <?php
  return ob_get_clean();
});

这些更新在初始化窗口全局设置对象并使用 WP Admin 中提供的数据填充它的每个元素之前写入一个 <script> 块。

注: 语法 <?=<?php echo 的简写


保存并退出文件。

现在, 在您的网络浏览器中检查 WordPress 页面。 这将向您显示页面的 HTML。 如果您 CTRL+F 并搜索 window.erwSettings,您将看到正在写入页面 HTML 的设置,如下所示:

...
  window.erwSettings = window.erwSettings || {};
  window.erwSettings["id5d5f1958aa5ae"] = {
    'color': '#cf6f1a',
    'name': 'sammy',
  }
...

从 React 中检索设置

在 React 应用程序中,您将根据 ID 检索设置并将边框颜色值作为属性 (prop) 传递给 App 组件。 这让 App 组件可以使用该值,而无需知道它来自何处。

打开index.js进行编辑:

sudo nano widget/src/index.js

更新它,使其包含以下突出显示的行:

/var/www/wordpress/wp-content/plugins/react-wordpress/widget/src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import * as serviceWorker from './serviceWorker';

const App = ({ settings }) => (
  <div className="App" style={{borderColor: settings.color}}>
    <span className="App__Message">Hello,<br />{settings.name}!</span>
  </div>
);

const targets = document.querySelectorAll('.erw-root');
Array.prototype.forEach.call(targets, target => {
  const id = target.dataset.id;
  const settings = window.erwSettings[id];
  ReactDOM.render(<App settings={settings} />, target)
});

serviceWorker.unregister();

保存文件并退出文本编辑器。

您的 React 应用程序现在将使用来自窗口全局 window.erwSettings 对象的唯一 ID 来检索设置并将它们传递给 App 组件。 要使其生效,请重新编译您的应用程序:

cd widget
sudo npm run build

完成最后一步后,在浏览器中刷新 WordPress 页面。 您将看到用户提供的边框颜色和服务器提供的显示名称出现在小部件中:

结论

在本教程中,您创建了自己的 WordPress 插件,其中包含一个 React 应用程序。 然后,您构建了一个短代码作为桥梁,使您的应用程序可嵌入到 WP Admin 页面构建器中,最后,您在页面上自定义了您的小部件。

现在,您可以放心地扩展您的 React 应用程序,因为您的交付机制已经到位。 WordPress 的这一基础确保您可以专注于客户端体验,并且随着您的应用程序扩展和增长,您可以轻松添加更多适用于任何 WordPress 安装的面向生产的工具和技术。

要进一步了解您可以使用坚实的 React 基础做什么,请尝试探索以下教程之一: