如何使用 React 和 WordPress 为您的 Web 应用程序构建皮肤

已发表: 2022-03-10
快速总结↬如果您一直在寻找内容驱动的解决方案,本文将解释如何使用 React 构建 SPA WordPress 主题。 继续阅读以了解为什么这对于您的 Web 应用程序的后端技术来说是一个不错的选择。

因此,您已经将自己培训为一名网络工程师,现在想要为您的客户建立一个快速的在线商店。 产品列表应该立即出现,搜索也应该不会浪费超过一秒钟。 那是白日梦的东西吗?

不再。 好吧,至少没有什么是结合 WordPress 的 REST API 和现代 JavaScript 库 React 无法实现的。

等等,什么? WordPress REST API?

是的,WordPress REST API 将帮助您为 Web 应用程序构建后端基础。 如果您正在构建内容驱动的解决方案,这对于您的 Web 应用程序的后端技术来说是一个不错的选择。 WordPress 也将与其他技术顺利互操作; 您可以使用 Node.js 作为应用程序的焦点来连接到其他 RESTful 服务。

WordPress REST API 是 WordPress 的游戏规则改变者,现在可以安全地称为 Web 应用程序框架,而不仅仅是 CMS。 现在前端和后端已经完全解耦,WordPress 可以用作移动应用程序后端,也可以用作任何专注于内容的系统的后端。

但为什么是 WordPress? 原因:您会对 WordPress 开箱即用的功能感到惊讶。 您将获得广泛的用户管理、媒体管理和一组对开发人员非常友好的 API,以扩展您的工作。

在本文中,我将引导您使用名为 React 的 JavaScript 库构建一个 SPA(单页应用程序)WordPress 主题,并连接到 WP REST API。

跳跃后更多! 继续往下看↓

让我们开始构建主题

本文假设您已经熟悉各种现有的 WordPress API,尤其是那些推动网站美学主题开发和网站插件功能开发的 API。 我还假设您已经为 WordPress 设置了开发平台,例如 Linux 或 MacOS 环境中的 LAMP 堆栈。

不过,为简单起见,我将仅提及在与 Windows 一起使用的 XAMPP 平台上可见的绝对路径。

现在,让我们在本地主机中创建一个 WordPress 实例,将其命名为“Celestial”。 这是 WordPress 主题的名称,我们将使用它来帮助我们确定构建未来主题的方向,以便与由 WordPress REST API 支持的 Web 应用程序一起使用。 您可能已经熟悉 WordPress 深受喜爱的模板层次结构,但使用 REST API,您有机会发现不同的东西!

然后我们需要在wp-content\themes文件夹中为主题创建一个文件夹。 导航到C:\xampp\htdocs\celestial\wp-content\themes\ (或等效)并创建一个文件夹celestial 。 在celestial主题文件夹中添加这些文件:

  1. index.php
    主题的包罗万象的文件。
  2. style.css
    这包含有关主题的信息(而不是实际的 CSS)。
  3. functions.php
    编写 CSS 和 JS 文件的功能和导入。

如果您希望在仪表板内显示主题图片,请添加名为screenshot.jpg的图像文件。

注意每个文件的代码只有几行,可以在 GitHub 上找到

接下来,登录到您的 WordPress 仪表板,前往外观主题并选择“天体”作为主题。 现在基础已经到位,让我们开始创建主题。

WordPress 主题选择器
您可以从仪表板的主题面板中选择您创建的“天体”主题。

开始使用 React 和 Webpack 为主题

React 是一个非常流行的 UI 库,由 Facebook 支持和维护。 根据 Stack Overflow 的 2017 年开发者调查结果,“React 是开发者最喜爱的。”

反应JS
React:用于构建用户界面的 JavaScript 库。

为了启动项目,您需要将项目初始化为 NPM(节点包管理器)项目。 这是通过终端中的命令npm init完成的(在您的计算机上安装了 Node.js 和 NPM 之后)。 初始化项目将提示您输入某些配置信息。 初始化成功后,NPM 会在主题的根目录下创建一个 package.json 文件。 该文件将包含项目详细信息和项目的所有依赖项。

React 现在在 MIT 许可下,所以我们将使用 React 16 版作为这个项目的 JavaScript 库。 React 在底层有一些很酷的特性,比如 Virtual DOM(内存中文档的表示),并且有许多围绕它的工具,比如 React Router。 React 也用于 WordPress 的项目 Calypso——WordPress.com 的仪表板。

我们现在将所需的 NPM 包安装到项目中:

  1. 在终端中输入npm install --save react react-dom并按 Enter 安装软件包。
    通过 CLI 安装 React
    使用 npm 安装 react 和 react-dom。
    由于我们将主题构建为单页应用程序 (SPA),因此我们需要 Webpack 等工具的帮助。 我们将编写代码作为不同的组件,Webpack 将帮助我们将它们打包在一起并将它们输出为单个 .js 或 .css 文件。 简而言之,它是一个模块捆绑器。

    Webpack 必须首先全局安装在您的计算机上。 为此,我们可以再次利用 NPM。
  2. 输入命令npm install -g webpack以在您的系统中全局安装最新的稳定版 Webpack。

    接下来,我们将在我们的应用程序中安装支持 Webpack 的 NPM 包。
  3. 转到我的 git repo 中的package.json文件,然后将其余的依赖项从那里复制到你的package.json的依赖项部分。 然后再次运行npm install以安装package.json中的所有包。
     { "name": "celestial", "version": "1.0.0", "description": "A basic theme using the WordPress REST API and React", "main": "index.js", "dependencies": { "babel-core": "^6.26.0", "babel-loader": "^7.1.2", "babel-preset-es2015": "^6.24.1", "babel-preset-react": "^6.24.1", "css-loader": "^0.28.7", "extract-text-webpack-plugin": "^3.0.1", "file-loader": "^1.1.5", "image-webpack-loader": "^3.4.2", "node-sass": "^4.5.3", "path": "^0.12.7", "react": "^16.0.0", "react-dom": "^16.0.0", "react-router-dom": "^4.2.2", "sass-loader": "^6.0.6", "style-loader": "^0.19.0", "url-loader": "^0.6.2", "webpack": "^3.6.0" }, "devDependencies": {}, "scripts": { "build": "webpack", "watch": "webpack --watch" }, "keywords": [ "blog", "decoupled", "react", "rest-api" ], "author": "Muhammad Muhsin", "license": "GPL-3.0" }

    以上是该项目的 package.json 文件中所有必需包的列表。
  4. 从 GitHub 复制配置并将其粘贴到主题文件夹的webpack.config.js文件中。
     var ExtractTextPlugin = require("extract-text-webpack-plugin"); var path = require('path'); module.exports = { entry: { app: './src/index.jsx' }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].js' }, module: { rules: [ { test: /\.scss$/, use: ExtractTextPlugin.extract({ fallback: 'style-loader', use: ['css-loader','sass-loader'], publicPath: 'dist' }) }, { test: /\.jsx?$/, exclude: /node_modules/, use: 'babel-loader' }, { test: /\.(jpe?g|png|gif|svg)$/i, use: [ 'file-loader?name=[name].[ext]&outputPath=images/&publicPath=https://localhost/celestial/wp-content/themes/celestial/dist/images', 'image-webpack-loader' ] }, { test: /\.(woff2?|svg)$/, loader: 'url-loader?limit=10000&name=fonts/[name].[ext]' }, { test: /\.(ttf|eot)$/, loader: 'file-loader?name=fonts/[name].[ext]' } ] }, resolve: { extensions: ['.js', '.jsx'] }, plugins: [ new ExtractTextPlugin({ filename: "style.css", allChunks: true }) ] }

    重要提示:请注意module.exportsmodulerules[3]usepublicPath可以根据项目的 dist 图像在 localhost 中的位置而改变。 如果这是错误的,图像可能无法在 Web 应用程序中正确显示。
  5. 之后,这些命令可用于编译项目:
    • webpacknpm run build编译项目,或者
    • webpack --watchnpm run watch编译项目并观察变化。

注意为了更好地理解 Webpack,请阅读 Joseph Zimmerman 在 Smashing Magazine 上的这篇文章

扩展 WordPress REST API

WordPress REST API 带有许多不同的端点,用于获取帖子、页面、媒体等。 但是,他们可能并不总是在回复中包含所有详细信息。 例如,posts 方法不提供特色图片的 URL 或作者姓名。 因此,我们必须对这些元素中的每一个进行单独的调用。

WordPress REST API
通过易于使用的 HTTP REST API 访问您的 WordPress 站点的数据。

但是,如果您想让自己的数据出现帖子响应中怎么办? 这就是扩展 REST API 的魅力所在。以下代码将在对帖子请求的响应中添加两个额外的变量,即author_namefeatured_image_src 。 代码在functions.php文件中:

 // Add various fields to the JSON output function celestial_register_fields() { // Add Author Name register_rest_field( 'post', 'author_name', array( 'get_callback' => 'celestial_get_author_name', 'update_callback' => null, 'schema' => null ) ); // Add Featured Image register_rest_field( 'post', 'featured_image_src', array( 'get_callback' => 'celestial_get_image_src', 'update_callback' => null, 'schema' => null ) ); // Add Published Date register_rest_field( 'post', 'published_date', array( 'get_callback' => 'celestial_published_date', 'update_callback' => null, 'schema' => null ) ); } add_action( 'rest_api_init', 'celestial_register_fields' ); function celestial_get_author_name( $object, $field_name, $request ) { return get_the_author_meta( 'display_name' ); } function celestial_get_image_src( $object, $field_name, $request ) { if($object[ 'featured_media' ] == 0) { return $object[ 'featured_media' ]; } $feat_img_array = wp_get_attachment_image_src( $object[ 'featured_media' ], 'thumbnail', true ); return $feat_img_array[0]; } function celestial_published_date( $object, $field_name, $request ) { return get_the_time('F j, Y'); }

使用 functions.php 文件中的其他变量扩展 REST API。

一个全局 JavaScript 变量

我们将在整个 React 应用程序中使用某些 WordPress 常量(或称为“变量”)。 这将是有关应用程序的各种路线的信息(稍后将成为 WooCommerce 特定的路线)。

该变量在functions.php文件中定义。 它将被称为 'CelestialSettings' 并附加到celestial-scripts ,即入队的app.js文件的句柄:

 wp_enqueue_script( 'celestial-script', get_stylesheet_directory_uri() . '/dist/app.js' , array(), '1.0', true ); $url = trailingslashit( home_url() ); $path = trailingslashit( parse_url( $url, PHP_URL_PATH ) ); wp_scripts()->add_data( 'celestial-script', 'data', sprintf( 'var CelestialSettings = %s;', wp_json_encode( array( 'title' => get_bloginfo( 'name', 'display' ), 'path' => $path, 'URL' => array( 'api' => esc_url_raw( get_rest_url( null, '/wp/v2' ) ), 'root' => esc_url_raw( $url ), ), 'woo' => array( 'url' => esc_url_raw( 'https://localhost/celestial/wp-json/wc/v2/' ), 'consumer_key' => 'ck_803bcdcaa73d3a406a0f107041b07ef6217e05b9', 'consumer_secret' => 'cs_c50ba3a77cc88c3bf46ebac49bbc96de3a543f03' ), ) ) ) );

将 WordPress (PHP) 变量传递到前端。

上面的代码显示了一个将 WordPress (PHP) 变量导入前端的示例,这是构建解耦主题时的一项重要且非常有用的技术。 该对象变量包含站点标题、路径、API 的 URL 和根以及与 WooCommerce 相关的三个变量(稍后解释)。

反应和 JSX

React 与其他主要的 JavaScript 库不同。 我的意思是,我们通常在 HTML 中编写 JavaScript。 然而,当涉及到 React 时,我们在 JavaScript 代码中编写 HTML。 更准确地说,我们在 JS 中编写 JSX。 JSX 与 HTML 非常相似,但有一些不同之处。 例如, class属性写为className 。 然后通过 Webpack 和 Babel 将其转换为纯 JavaScript 并保存在app.js中。

然而,编写 JSX 有一些限制。 例如,我们的render()方法中只能有一个子元素,它将作为组件的根元素。 但是,优点是更容易调试。 我们可以确切地知道我们在哪里犯了错误,而在普通 HTML 中,我们的错误通常不会明确显示。 我们将为这个项目编写 JSX,因此 JavaScript 文件的扩展名为.jsx 。 但是,如果您愿意,它也可以是.js

src文件夹中创建以下文件:

  1. index.jsx (主文件和包含 React Router 配置的文件)
  2. header.jsx (标题组件)
  3. footer.jsx (页脚组件)
  4. posts.jsx (用于帖子存档)
  5. post-list.jsxposts.jsx中单个帖子的组件)
  6. post.jsx (用于单个帖子)
  7. products.jsx (包含来自 WooCommerce 的所有产品)
  8. product.jsx (显示来自 WooCommerce 的单个产品)
  9. style.scss (包含所有 SASS 格式的 CSS 代码)
src文件夹的文件夹结构
Celestial 项目中 src 文件夹的文件夹结构。

ReactDOM.render()

index.jsx文件是项目的根目录。 我的意思是, index.jsx包含渲染到 DOM 的组件 App。

 import { render } from 'react-dom'; // importing render from ReactDOM const App = () => ( // defining the routes <div> <Header /> <div> <Switch> <Route exact path={CelestialSettings.path} component={Posts} /> // the root path </Switch> </div> <Footer /> </div> ); // React Router const routes = ( <Router> <Route path="/" component={App} /> </Router> ); render( // rendering to the DOM by replacing #page with the root React component (routes), document.getElementById('page') // rendering the route );

其他组件在 React Router 中指定,将在访问不同的路由时加载。

这就是我们编写模块化组件的方式,所有不同的组件最终都以index.jsx结尾。

有状态与无状态组件

您会注意到组件是以以下两种方式之一编写的:

  1. const App = () => (
  2. class Post extends React.Component {

第一种方法是我们如何编写无状态组件,第二种是有状态组件的示例。 无状态意味着组件中没有“状态”。 'state'本质上是一个在组件内部有信息的变量,每次变量变化时,组件都会重新渲染。 有状态组件也称为“智能组件”。 因此,状态变量用于该组件内的内部通信。

第二种类型,无状态组件中没有状态变量,有时称为“哑组件”。 但是,与有状态组件一样,它们具有“道具”,这是从其父组件传递给它们的属性。

有状态组件具有 React 生命周期方法,而无状态组件只有render()方法,这是它的默认方法。

React 生命周期方法

这些是在组件生命周期的不同阶段调用的方法,我们可以覆盖这些方法以在这些实例中运行我们自己的代码。 我们在我们的应用程序中使用以下方法:

  • constructor()
    在安装组件之前调用。
  • componentDidMount()
    在安装组件后立即调用。
  • render()
    为呈现 JSX (HTML) 内容而调用的方法。
  • componentDidUpdate()
    在组件更新时调用。
  • componentWillUnmount()
    当要删除组件时调用。

注意要了解有关组件及其生命周期的更多信息,请阅读此处的文档

JavaScript 承诺

我们将使用 JavaScript Promises 从我们的 WordPress REST API 获取数据。 首先,我们的functions.php中有 REST API 的 URL,我们将它作为一个 JavaScript 变量附加在其中,我们可以从前端访问它。

我们将使用 JavaScript 的 fetch API 方法从不同的端点获取数据。 我们正在添加一个加载器以在获取内容时向用户显示:

 getMorePosts() { var that = this; var totalPages; // adding a loader jQuery("#loader").addClass("active"); this.setState({ page: this.state.page + 1 }); fetch(CelestialSettings.URL.api + "/posts/?page=" + this.state.page) .then(function (response) { for (var pair of response.headers.entries()) { // getting the total number of pages if (pair[0] == 'x-wp-totalpages') { totalPages = pair[1]; } if (that.state.page >= totalPages) { that.setState({ getPosts: false }) } } if (!response.ok) { throw Error(response.statusText); } return response.json(); }) .then(function (results) { var allPosts = that.state.posts.slice(); results.forEach(function (single) { allPosts.push(single); }) that.setState({ posts: allPosts }); // removing the loader jQuery("#loader").removeClass("active"); }).catch(function (error) { console.log('There has been a problem with your fetch operation: ' + error.message); jQuery("#loader").remove(); }); }

从各个端点获取数据,加载程序指示进程正在运行。

使用 React 路由器

React Router 是为我们的应用程序处理客户端路由的库。 使用 WordPress 可以实现服务器端路由,但要实现真正的 SPA 体验,我们需要 React Router 的帮助。

从版本 4 开始,React Router 被分成三个包: react-routerreact-router-domreact-router-native 。 我们将在这个项目中使用react-router-dom ,因为它是在 Web 应用程序中使用的。

由于已经安装了react-router-dom ,我们可以在index.jsx文件中编写路由器配置。 代码如下:

 const App = () => ( <div> <Header /> <div> <Switch> <Route exact path={CelestialSettings.path} component={Posts} /> <Route exact path={CelestialSettings.path + 'posts/:slug'} component={Post} /> <Route exact path={CelestialSettings.path + 'products'} component={Products} /> <Route exact path={CelestialSettings.path + 'products/:product'} component={Product} /> <Route path="*" component={NotFound} /> </Switch> </div> <Footer /> </div> ); // Routes const routes = ( <Router> <Route path="/" component={App} /> </Router> ); render( (routes), document.getElementById('page') );

index.jsx 文件中的路由器配置。

上面的代码将负责在客户端处理的所有路由。 最后一行中的*表示上面未提及的任何其他路线都会将用户带到“404 Not Found”页面。

<Link to="">标签代替<a href=””>标签用于使用 React Router 在不同页面之间进行链接:

 <div className="navbar-nav"> <Link className="nav-item nav-link active" to={CelestialSettings.path} >Home <span className="sr-only">(current)</span></Link> <Link className="nav-item nav-link" to={CelestialSettings.path + "products/"} >Products</Link> </div>

使用<Link to="">标签在不同页面之间进行链接。

获取测试数据

现在您已经创建了主题,是时候添加一些数据了。 添加数据的一种方法是创建我们自己的内容。 但是,有一种更简单(更好)的方法可以将数据添加到我们的 WordPress 网站。 此方法从外部源导入占位符数据:

  • 前往 https://codex.wordpress.org/Theme_Unit_Test 并下载主题单元测试数据
  • 前往工具 > 导入 > WordPress安装 WordPress 导入器。
  • 安装 WordPress 导入器后,单击运行导入器。
  • 在导入器中单击“选择文件”
  • 选择下载的文件并导入 WordPress 主题单元测试数据

现在您必须选择theme-unit-test-data.xml文件,所有占位符内容现在都在您的站点上。

成功导入占位符内容
当所有数据都正确导入时。

WooCommerce 集成

现在,我们已经准备好使用 React 为我们的商店提供动力了。 为此,我们将使用products.jsxproduct.jsx文件,它们的代码分别类似于post.jsxpost.jsx

我们将在“woo”下的 CelestialSettings 添加另外三个变量(请参阅全局 JavaScript 变量):

  1. 网址
  2. consumer_key
  3. consumer_secret

Consumer key 和 Consumer secret 必须从DashboardWooCommerceSettingsAPIKeys/Apps生成。

对于 woo URL,您必须手动添加它(因为 WooCommerce 仅允许通过 SSL 传输,所以添加带有 https 的 URL,即https://localhost/celestial/wp-json/wc/v2/ )。

复制 Consumer 密钥和 Consumer Secret 并将它们粘贴到functions.php中的适当位置。 这将作为通过 API 调用访问 WooCommerce 的身份验证。

有关其 API 的更多信息,请访问 WooCommerce REST API 文档。 products.jsx文件包含用产品填充商店的代码。 管理员可以从仪表板添加产品。 只需转到仪表板产品添加新产品并输入产品详细信息。

添加新产品
通过仪表板添加新产品。

当您单击某个产品时,您将被带到product.jsx页面:

个别产品页面
基于 product.jsx 文件呈现的单个产品页面

上述页面的代码类似于post.jsx

 renderProduct() { return ( <div className="card"> <div className="card-body"> <div className="col-sm-4"><img className="product-image" src={this.state.product.images ? this.state.product.images[0].src : null} alt={this.state.product.images ? this.state.product.images[0].alt : null } /></div> <div className="col-sm-8"> <h4 className="card-title">{this.state.product.name}</h4> <p className="card-text"><strike>${this.state.product.regular_price}</strike> <u>${this.state.product.sale_price}</u></p> <p className="card-text"><small className="text-muted">{this.state.product.stock_quantity} in stock</small></p> <p className="card-text">{jQuery(this.state.product.description).text()}</p> </div> </div> </div> ); }

product.jsx 文件的代码" alt="product.jsx 文件的代码

永久链接

为了使主题正常工作,我们必须在DashboardSettingsPermalinks中设置以下永久链接:

  1. Common SettingsCustom Structure下: https://localhost/celestial/posts/%postname%/

  2. 产品永久链接自定义基础下: /products/

如果您未按上述方式设置永久链接,则主题可能无法按预期运行。

WooCommerce 修复

当您导航到localhost/celestial/products时,您可能会得到一个应该加载产品的空白区域。 发生这种情况是因为 WooCommerce 需要经过身份验证的请求,而我们的 localhost 不是 https。 要解决此问题:

  1. 访问 https://localhost/celestial/wp-json/wc/v2/products。 这会给我们一个警告:
当 localhost 不是 https 时发出警告
WooCommerce 需要经过身份验证的请求,如果 localhost 不是 https,则会显示警告
  1. 单击高级 > 继续到本地主机(不安全)
  2. 现在,如果您返回产品页面,商品将正确显示。

注意如果您在 Mac 上使用 Valet,则必须在您的站点上运行 Valet Secure 以使用 TLS 证书保护本地站点。 这是解决问题的另一种方法。

什么是 ScrollMagic?

ScrollMagic 是一个库,它允许我们在滚动页面时执行某些操作。 要使用 ScrollMagic,我们将在functions.php中加入 ScrollMagic JavaScript 库。 我们在这个项目的两个实例中使用了 ScrollMagic:

  1. 要在posts.jsx组件中延迟加载帖子:
     componentDidMount() { var that = this; window.onbeforeunload = function () { window.scrollTo(0, 0); } // init ScrollMagic Controller that.state.controller = new ScrollMagic.Controller(); // build scene var scene = new ScrollMagic.Scene({ triggerElement: "#colophon", triggerHook: "onEnter" }) .addTo(that.state.controller) .on("enter", function (e) { if (that.state.getPosts && that.getMorePosts !== null) { that.getMorePosts(); } }); }

    在 posts.jsx 组件中延迟加载帖子
  2. 通过分别在posts.jsxproducts.jsx组件中滚动帖子和产品来显示帖子的淡入动画:
     componentDidUpdate() { var FadeInController = new ScrollMagic.Controller(); jQuery('.posts-container .col-md-4.card-outer').each(function () { // build a scene var FadeInScene = new ScrollMagic.Scene({ triggerElement: this.children[0], reverse: false, triggerHook: 1 }) .setClassToggle(this, 'fade-in') .addTo(FadeInController); }); }

    为出现滚动的帖子应用淡入动画

我们现在准备从前端查看我们的主题。 在您的 Web 浏览器上导航到localhost/celestial并查看您的主题。

拍拍自己的后背,因为您现在已经成功创建了主题!

其他带有 JavaScript 库的 WordPress 主题

如果你觉得这很有帮助,你可以看看使用现代 JavaScript 库/框架构建的其他很棒的解耦 WordPress 主题:

  • Foxhound:第一个进入 WordPress 主题存储库的解耦主题。 这个主题由 Kelly Dwan 编写,使用 React、Redux 和 React Router。
  • Anadama React:同一作者的另一个主题,但使用 Flux 代替 Redux,使用 Page 代替 React Router。
  • Wallace:由 Kirby 编写,这个主题使用 Angular 和 WordPress REST API。
  • Picard:由 Automattic 自己编写,用于展示 WordPress REST API 的功能。
  • React Verse:我基于 Foxhound 编写的 React 和 Redux 主题。

下一步

我想向您展示的主要部分现在已经完成。 您可以继续构建项目以获取更多知识。 这些是您可以遵循的一些建议:

  1. 带有 WooCommerce 插件的成熟商店,包括结账和购物车;
  2. 归档、标签、分类等各页面;
  3. 使用 Redux 或 Flux 进行状态管理。

祝你好运,编码愉快!