管理任何 WordPress 应用程序的状态(如何处理和组织数据)都是一项挑战。随着项目的发展,跟踪数据流并确保各组件更新的一致性变得越来越困难。WordPress 数据包可以在这方面提供帮助,因为它为状态管理提供了一个强大的解决方案。
本文将介绍 WordPress 数据包,探讨其关键概念、实施策略和最佳实践。
介绍WordPress数据包
WordPress 数据包(正式名称为@wordpress/data
)是一个 JavaScript(ES2015 及更高版本)状态管理库,为管理应用程序状态提供了一种可预测的集中方式。正确的实现有助于更轻松地构建复杂的用户界面,并处理整个应用程序的数据流。
WordPress 数据包的灵感来自 React 生态系统中流行的状态管理库 Redux。
在这里,数据模块在 WordPress 环境中运行,并提供与 WordPress 特定功能和 API 的集成。如果您为 WordPress 区块编辑器进行构建(或者您必须支持它),那么该软件包对于管理其状态至关重要。通过在自己的插件和主题中使用相同的工具和模式,您可以创建更加一致和熟悉的开发体验。
数据包与Redux的关系
虽然 WordPress 数据包从 Redux 中汲取了灵感,但它并不是直接移植过来的。为了适应 WordPress 生态系统,数据包做了很多调整,两种解决方案之间也存在一些关键差异:
- 数据包旨在与 WordPress 应用程序接口和功能无缝协作,而 Vanilla Redux 则无法做到这一点。
- 与 Redux 相比,数据包提供了更精简的 API。这可以让你更容易上手。
- 与 Redux 不同,数据包内置了对异步操作的支持。如果你使用 WordPress REST API,这将非常有用。
WordPress 数据包与 REST API 也有一些可比性。虽然它们都涉及数据管理,但用途不同:
- WordPress REST API提供了一种通过HTTP与WordPress数据交互的方式。您可以将其用于外部应用程序、无头 WordPress 设置以及任何需要检索和操作数据的地方。
- WordPress 数据包为数据和用户界面状态提供了一个集中存储空间。它是在应用程序中处理数据流和更新的一种方式。
在许多情况下,您会同时使用这两种方法:使用 REST API 在服务器上获取和更新数据,使用 WordPress 数据包在应用程序中管理这些数据。
WordPress数据包的关键概念和术语
WordPress 数据包提供了一种直观的状态管理方式。这是指存储中的数据。它代表了应用程序的当前状态,可以包括用户界面状态(如是否有打开的模态)和数据状态(如帖子列表)。
在这种情况下,存储是 WordPress 数据包的中心枢纽。它保存网站的整个状态,并提供访问和更新该状态的方法。在 WordPress 中,您可以拥有多个存储空间。每个存储将负责网站的特定区域。
为了管理这些存储,您需要一个注册表。这个中心对象提供了注册新存储和访问现有存储的方法。注册表将保存存储,而这些存储将保存应用程序状态。
处理状态有几种方法:
- 操作描述状态的变化。这些是普通的 JavaScript 对象,是触发状态更新的唯一方式。操作通常会有一个
type
属性,用于描述操作。它还可能包含其他数据。 - 选择器从存储中提取特定的状态片段。通过这些函数,你可以访问状态数据,而无需直接与存储结构交互。解析器(Resolvers)与之相关,用于处理异步数据获取。使用这些功能可以确保在运行选择器之前访问存储中的所需数据。
- 还原器指定状态应如何改变以响应操作。还原器将当前状态和操作作为参数,并返回一个新的状态对象。控制函数可让还原器在不产生副作用的情况下处理复杂的异步操作。
你需要理解这些基本概念,因为它们共同作用,创建了一个以存储为核心的强大的状态管理系统。
存储:WordPress数据包的中心枢纽
存储是应用程序状态的容器,并提供与之交互的方法。WordPress 数据包捆绑了其他几个包,每个包都为块目录、区块编辑器、核心、文章编辑等注册了存储。
每个存储都有一个独特的命名空间,如core
、core/
editor
和 core/notices
。第三方插件也会注册存储空间,因此需要选择唯一的命名空间以避免冲突。无论如何,你注册的存储空间在大多数情况下都会存在于默认注册表中。
这个中心对象有几个职责:
- 注册新商店。
- 提供对现有商店的访问。
- 管理对状态变化的订阅。
虽然您不会经常与注册表进行直接交互,但您确实需要了解它在数据包如何协调整个 WordPress 的状态管理中的作用。
与WordPress数据存储的基本交互
如果您使用 ES2015+JavaScript,并且正在使用 WordPress 插件或主题,您可以将其作为依赖项:
npm install @wordpress/data --save
在代码中,您将从文件顶部的软件包中导入必要的函数:
import { select, dispatch, subscribe } from '@wordpress/data';
与现有 WordPress 商店交互时,需要使用一些导入的功能。例如,使用 select
访问状态数据:
const posts = select('core').getPosts();
调度操作也是如此:
dispatch('core').savePost(postData);
订阅状态更改使用的格式略有不同,但概念相同:
subscribe(() => { const newPosts = select('core').getPosts(); // Update your UI based on the new posts });
不过,您并不总是使用默认商店。通常情况下,您需要使用现有的附加商店或注册自己的商店。
如何注册WordPress数据存储
定义您的商店配置并将其注册到 WordPress 数据包中,首先要导入 register
函数:
… import { createReduxStore, register } from '@wordpress/data'; …
它只需要一个参数–你的商店描述符。接下来,你应该为商店定义一个默认状态,以设置其默认值:
… const DEFAULT_STATE = { todos: [], }; …
接下来,创建一个 actions
对象,定义一个用于处理状态更新的 reducer
函数,并创建一个带有访问状态数据函数的 selectors
对象:
const actions = { addTodo: (text) => ({ type: 'ADD_TODO', text, }), }; const reducer = (state = DEFAULT_STATE, action) => { switch (action.type) { case 'ADD_TODO': return { ...state, todos: [...state.todos, { text: action.text, completed: false }], }; default: return state; } }; const selectors = { getTodos: (state) => state.todos, };
要创建商店配置,请使用 createReduxStore
对象进行定义。这将初始化商店的操作、选择器、控件和其他属性:
const store = createReduxStore('my-plugin/todos', { reducer, actions, selectors, });
该对象最起码需要一个还原器来定义状态的形状,以及状态如何随其他操作而变化。最后,通过调用 createReduxStore
定义的存储描述符来注册存储:
register(store);
现在,您可以像与其他人一样与自定义商店进行互动:
import { select, dispatch } from '@wordpress/data'; // Add a new todo dispatch('my-plugin/todos').addTodo('Learn WordPress data package'); // Get all todos const todos = select('my-plugin/todos').getTodos();
分解WordPress数据存储的五个属性
使用 WordPress 数据包的大部分工作都是“逆向”进行的–在存储本身之前定义底层数据存储属性。createReduxStore
对象就是一个很好的例子,因为它汇集了所有定义,以便创建用于注册存储的描述符:
import { createReduxStore } from '@wordpress/data'; const store = createReduxStore( 'demo', { reducer: ( state = 'OK' ) => state, selectors: { getValue: ( state ) => state, }, } );
这些其他属性也需要设置和配置。
1. 动作
动作是触发商店状态变化的主要方式。它们是简单的 JavaScript 对象,描述了应该发生的事情。因此,最好先创建这些动作,因为您可以决定要检索哪些状态。
const actions = { addTodo: (text) => ({ type: 'ADD_TODO', text, }), toggleTodo: (index) => ({ type: 'TOGGLE_TODO', index, }), };
动作创建器采用可选参数,并将返回一个对象,以便传递给您定义的还原器:
const actions = { updateStockPrice: (symbol, newPrice) => { return { type: 'UPDATE_STOCK_PRICE', symbol, newPrice }; },
如果传入存储描述符,就可以分派动作创建器并更新状态值:
dispatch('my-plugin/todos').updateStockPrice('¥', '150.37');
将动作对象视为指示还原器如何更改状态的指令。至少,您可能需要定义创建、更新、读取和删除(CRUD)操作。您也可以为动作类型创建一个单独的 JavaScript 文件,并为所有这些类型创建一个对象,尤其是当您将它们定义为常量时。
2. 还原器
这里值得谈谈还原器,因为它与动作一起扮演着核心角色。它的工作是指定状态应该如何变化,以响应从动作中得到的指令。如果将动作指令和当前状态传递给它,它就能返回一个新的状态对象,并将其沿链传递:
const reducer = (state = DEFAULT_STATE, action) => { switch (action.type) { case 'ADD_TODO': return { ...state, todos: [...state.todos, { text: action.text, completed: false }], }; case 'TOGGLE_TODO': return { ...state, todos: state.todos.map((todo, index) => index === action.index ? { ...todo, completed: !todo.completed } : todo ), }; default: return state; } };
需要注意的是,还原器必须是一个纯函数,它不应该改变它所接受的状态(相反,它应该返回更新后的状态)。还原器和操作在很多方面都是共生关系,因此掌握它们如何协同工作非常重要。
3. 选择器
为了从注册存储访问当前状态,你需要选择器。这是“暴露”商店状态的主要方式,有助于保持组件与商店内部结构的分离:
const selectors = { getTodos: (state) => state.todos, getTodoCount: (state) => state.todos.length, };
您可以使用 select
函数调用这些选择器:
const todoCount = select('my-plugin/todos').getTodoCount();
然而,选择器不会将数据发送到任何地方:它只是显示数据并提供访问。
选择器可以接收尽可能多的参数,以便准确地访问状态。它返回的值是这些参数在你定义的选择器中实现的结果。和操作一样,你可能会选择用一个单独的文件来保存所有的选择器,因为可能会有很多这样的选择器。
4. 控件
指导网站功能的执行流程或执行其中的逻辑,就是使用控件的地方。这些控件定义了操作执行流程的行为。将这些控件视为 WordPress 数据包中的助手,因为它们是收集状态传递给解析器的中间人。
控件还能处理商店中的副作用,例如 API 调用或与浏览器 API 的交互。通过它们,您可以保持还原器的清洁,同时还能处理复杂的异步操作:
const controls = { FETCH_TODOS: async () => { const response = await fetch('/api/todos'); return response.json(); }, }; const actions = { fetchTodos: () => ({ type: 'FETCH_TODOS' }), };
这种获取和返回数据的循环对整个流程至关重要。但是,如果没有动作的调用,就无法使用这些数据。
5. 解析器
选择器会显示存储的状态,但不会明确地将数据发送到任何地方。解析器与选择器(和控件)连接,以检索数据。与控件一样,它们也可以处理异步数据获取。
const resolvers = { getTodos: async () => { const todos = await controls.FETCH_TODOS(); return actions.receiveTodos(todos); }, };
在运行选择器之前,解析器会确保您所要求的数据在存储区中可用。解析器和选择器之间的这种紧密联系意味着它们需要匹配名称。这样,WordPress 数据包就能根据您请求的数据了解要调用哪个解析器。
此外,解析器将始终接收您传入选择器函数的相同参数,并返回、生成或分派动作对象。
使用WordPress数据包时的错误处理
在使用 WordPress 数据包时,您必须执行适当的错误处理。如果您选择处理异步操作、使用全栈部署或进行 API 调用,那么这一点就更加重要。
例如,如果您分派的操作涉及异步操作,那么 try-catch 块就是一个不错的选择:
const StockUpdater = () => { // Get the dispatch function const { updateStock, setError, clearError } = useDispatch('my-app/stocks'); const handleUpdateStock = async (stockId, newData) => { try { // Clear any existing errors clearError(); // Attempt to update the stock await updateStock(stockId, newData); } catch (error) { // Dispatch an error action if something goes wrong setError(error.message); } }; return ( <button onClick={() => handleUpdateStock('AAPL', { price: 150 })}> Update Stock </button> ); };
对于还原器,可以处理错误操作并更新状态:
const reducer = (state = DEFAULT_STATE, action) => { switch (action.type) { // ... other cases case 'FETCH_TODOS_ERROR': return { ...state, error: action.error, isLoading: false, }; default: return state; } };
在使用选择器时,您可以加入错误检查功能来处理潜在问题,然后在使用数据前检查组件中是否存在错误:
const MyComponent = () => { // Get multiple pieces of state including error information const { data, isLoading, error } = useSelect((select) => ({ data: select('my-app/stocks').getStockData(), isLoading: select('my-app/stocks').isLoading(), error: select('my-app/stocks').getError() })); // Handle different states if (isLoading) { return <div>Loading...</div>; } if (error) { return ( <div className="error-message"> <p>Error loading stocks: {error.message}</p> <button onClick={retry}>Try Again</button> </div> ); } return ( <div> {/* Your normal component render */} </div> ); };
useSelect
和 useDispatch
函数为您提供了在 WordPress 数据包中处理错误的强大功能。通过这两个函数,您可以将自定义错误信息作为参数传递。
良好的做法是确保在初始配置时集中管理错误状态,并将错误边界保持在组件级别。对加载状态采用错误处理也有助于保持代码的清晰和一致。
如何将WordPress数据存储与网站集成
WordPress 数据包可以帮助您管理状态。将所有这些整合在一起也是一个实际的考虑因素。让我们来看一个实时显示和更新财务数据的股票行情器。
首要任务是为数据创建一个存储空间:
import { createReduxStore, register } from '@wordpress/data'; const DEFAULT_STATE = { stocks: [], isLoading: false, error: null, }; const actions = { fetchStocks: () => async ({ dispatch }) => { dispatch({ type: 'FETCH_STOCKS_START' }); try { const response = await fetch('/api/stocks'); const stocks = await response.json(); dispatch({ type: 'RECEIVE_STOCKS', stocks }); } catch (error) { dispatch({ type: 'FETCH_STOCKS_ERROR', error: error.message }); } }, }; const reducer = (state = DEFAULT_STATE, action) => { switch (action.type) { case 'FETCH_STOCKS_START': return { ...state, isLoading: true, error: null }; case 'RECEIVE_STOCKS': return { ...state, stocks: action.stocks, isLoading: false }; case 'FETCH_STOCKS_ERROR': return { ...state, error: action.error, isLoading: false }; default: return state; } }; const selectors = { getStocks: (state) => state.stocks, getStocksError: (state) => state.error, isStocksLoading: (state) => state.isLoading, }; const store = createReduxStore('my-investing-app/stocks', { reducer, actions, selectors, }); register(store);
此过程会设置一个默认状态,其中包括错误和加载状态,以及操作、还原器和选择器。定义好这些后,就可以注册商店了。
显示商店数据
有了商店,您就可以创建一个 React 组件来显示其中的信息:
import { useSelect, useDispatch } from '@wordpress/data'; import { useEffect } from '@wordpress/element'; const StockTicker = () => { const stocks = useSelect((select) => select('my-investing-app/stocks').getStocks()); const error = useSelect((select) => select('my-investing-app/stocks').getStocksError()); const isLoading = useSelect((select) => select('my-investing-app/stocks').isStocksLoading()); const { fetchStocks } = useDispatch('my-investing-app/stocks'); useEffect(() => { fetchStocks(); }, []); if (isLoading) { return <p>Loading stock data...</p>; } if (error) { return <p>Error: {error}</p>; } return ( <div className="stock-ticker"> <h2>Stock Ticker</h2> <ul> {stocks.map((stock) => ( <li key={stock.symbol}> {stock.symbol}: ${stock.price} </li> ))} </ul> </div> ); };
该组件引入了 useSelect
和 useDispatch
钩子(以及其他钩子),用于处理数据访问、调度操作和组件生命周期管理。它还设置了自定义错误和加载状态消息,以及一些实际显示行情的代码。有了这些,您现在需要向 WordPress 注册组件。
在WordPress中注册组件
如果不在WordPress中注册,您就无法使用您创建的组件。这意味着要将其注册为一个块(Block),不过如果您为经典主题设计的话,也可以将其注册为一个小部件(widget)。本示例使用的是区块。
import { registerBlockType } from '@wordpress/blocks'; import { StockTicker } from './components/StockTicker'; registerBlockType('my-investing-app/stock-ticker', { title: 'Stock Ticker', icon: 'chart-line', category: 'widgets', edit: StockTicker, save: () => null, // This will render dynamically });
这个过程将沿用在 WordPress 中注册区块的典型方法,不需要任何特殊的实施或设置。
管理状态更新和用户交互
一旦注册了区块,就必须处理用户交互和实时更新。这需要一些交互式控件以及自定义 HTML 和 JavaScript:
const StockControls = () => { const { addToWatchlist, removeFromWatchlist } = useDispatch('my-investing-app/stocks'); return ( <div className="stock-controls"> <button onClick={() => addToWatchlist('AAPL')}> Add Apple to Watchlist </button> <button onClick={() => removeFromWatchlist('AAPL')}> Remove from Watchlist </button> </div> ); };
对于实时更新,您可以在 React 组件中设置间隔时间:
useEffect(() => { const { updateStockPrice } = dispatch('my-investing-app/stocks'); const interval = setInterval(() => { stocks.forEach(stock => { fetchStockPrice(stock.symbol) .then(price => updateStockPrice(stock.symbol, price)); }); }, 60000); return () => clearInterval(interval); }, [stocks]);
这种方法既能保持组件数据与商店数据同步,又能明确区分关注点。WordPress 数据包将处理所有的状态更新,从而保证应用程序的一致性。
服务器端呈现
最后,您可以设置服务器端呈现,以确保页面加载时库存数据是最新的。这需要一定的 PHP 知识:
function my_investing_app_render_stock_ticker($attributes, $content) { // Fetch the latest stock data from your API $stocks = fetch_latest_stock_data(); ob_start(); ?> <div class="stock-ticker"> <h2>Stock Ticker</h2> <ul> <?php foreach ($stocks as $stock) : ?> <li><?php echo esc_html($stock['symbol']); ?>: $<?php echo esc_html($stock['price']); ?></li> <?php endforeach; ?> </ul> </div> <?php return ob_get_clean(); } register_block_type('my-investing-app/stock-ticker', array( 'render_callback' => 'my_investing_app_render_stock_ticker' ));
这种方法可将您的数据存储与 WordPress 完全集成,处理从初始呈现到实时更新和用户交互的所有事务。
小结
WordPress 数据包是管理项目应用程序状态的一种复杂而强大的方法。除了关键概念之外,还有函数、运算符、参数等等。不过请记住,并不是所有数据都需要放在全局存储中–本地组件状态在你的代码中仍有一席之地。
你是经常使用 WordPress 数据包,还是有其他管理状态的方法?请在下面的评论中与我们分享您的观点。
评论留言