gutenbergdocs/reference-guides/interactivity-api/core-concepts/the-reactive-and-declarative-mindset.md
2025-10-22 01:33:45 +08:00

318 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

### 可变性与不可变性
与许多其他响应式框架不同,**交互式 API 在更新全局状态或本地上下文时不需要使用不可变性**。您可以直接修改对象和数组,响应式系统仍将按预期工作。这在许多情况下可以使代码更直观和简洁。
例如,您可以像这样向数组添加新项:
```javascript
const { state } = store( 'myArrayPlugin', {
state: {
list: [ '项目1', '项目2' ],
},
actions: {
addItem() {
// 正确做法:
state.list.push( '新项目' );
// 错误做法:
state.list = [ ...state.list, '新项目' ]; // 不要这样做!
},
},
} );
```
无需像在其他框架中那样创建新数组或使用展开运算符。交互式 API 将检测此更改并更新依赖于 `state.list` 的任何 UI 部分。
### 响应式副作用
除了自动更新 UI 之外,交互式 API 还允许您使用 `data-wp-watch` 等指令在响应式数据更改时执行副作用。副作用对于日志记录、进行 API 调用或更新与 UI 不直接相关的应用程序其他部分等任务非常有用。
以下是使用 `data-wp-watch` 的示例:
```html
<div
data-wp-interactive="myCounterPlugin"
data-wp-context='{ "counter": 0 }'
data-wp-watch="callbacks.logCounter"
>
<p>计数器:<span data-wp-text="context.counter"></span></p>
<button data-wp-on--click="actions.increment">增加</button>
</div>
```
```javascript
store( 'myCounterPlugin', {
actions: {
increment() {
const context = getContext();
context.counter += 1;
},
},
callbacks: {
logCounter: () => {
const context = getContext();
console.log( `计数器当前值为:${ context.counter }` );
},
},
} );
```
在此示例中:
1. `data-wp-context` 指令添加了一个本地上下文,其中包含属性 `counter`,其值为 `0`
2. `data-wp-watch` 指令设置为 `callbacks.logCounter`
3. 每次 `context.counter` 更改时,`logCounter` 回调都将执行。
4. `logCounter` 回调将当前计数器值记录到控制台。
这使您可以创建声明式副作用,这些副作用会自动响应数据更改而运行。`data-wp-watch` 的其他一些用例可能包括:
- 在数据更改时将数据保存到 `localStorage`
- 发送分析事件。
- 为无障碍目的更改焦点。
- 更新页面标题、元标记或 `<body>` 属性。
- 触发动画。
## 结论
在使用交互式 API 时,请记住要从状态、操作和副作用的角度思考。定义您的数据,描述应如何更改,然后让交互式 API 处理其余工作。这种思维转变可能需要一些时间,特别是如果您习惯了更命令式的编程风格,但通过接受它,您将释放交互式 API 的全部潜力,以创建真正动态和交互式的 WordPress 块,让您的用户感到愉悦。
### 你能发现这个错误吗?
在命令式示例中,为了教学目的故意引入了一个错误。你能找到它吗?这可不容易!
<details>
<summary>查看答案!</summary>
如果先按下显示按钮,接着按下激活按钮,最后再按下隐藏按钮,代码不会通过`statusParagraph.classList.add('inactive');`添加`inactive`类。因此,当用户下次按下显示按钮时,段落文本将不会显示为红色。
</details>
这类错误在命令式代码中非常常见因为你需要手动控制所有条件。而在声明式代码中则不存在这类问题因为框架会负责DOM更新永远不会遗漏任何细节。
### 声明式方法的优势
如示例所示命令式方法需要详细步骤并直接操作DOM随着交互复杂度的增加代码会迅速变得复杂且难以维护。可能的状态和元素越多需要添加的条件逻辑就越多代码复杂度呈指数级增长。而声明式方法通过状态管理和框架处理DOM更新来简化流程从而产生更易读、易维护和可扩展的代码。
## 响应式系统
得益于对响应式特性的运用交互式API是一个声明式框架。在响应式系统中数据的变更会自动触发用户界面的更新确保视图始终反映应用程序的当前状态。
### 响应式工作原理
交互式API采用细粒度响应式系统其运作方式如下
1. **响应式状态**在交互式API中全局状态和本地上下文都是响应式的。这意味着当这些数据源发生变化时依赖它们的任何UI部分都会自动更新。
- **全局状态**:这是可在整个交互块中访问的全局数据
- **本地上下文**:这是特定元素及其子元素专属的本地数据
- **派生状态**:除了基础状态属性外,您还可以定义计算属性,这些属性会在其依赖项变更时自动更新
_请访问[理解全局状态、本地上下文和派生状态](/docs/reference-guides/interactivity-api/core-concepts/undestanding-global-state-local-context-and-derived-state.md)指南详细了解如何在交互式API中使用不同类型的响应式状态。_
2. **操作**:这些通常由事件处理程序触发的函数,用于变更全局状态或本地上下文
3. **响应式绑定**:使用特殊属性(如`data-wp-bind`、`data-wp-text`或`data-wp-class`将HTML元素与响应式状态值绑定
4. **自动更新**当操作变更全局状态或本地上下文时交互式API会自动更新依赖该状态的所有DOM部分直接依赖或通过派生状态间接依赖
让我们通过分析之前的示例来解析这些概念:
```javascript
const { state } = store( 'myInteractivePlugin', {
state: {
isVisible: false,
isActive: false,
get visibilityText() {
return state.isVisible ? 'hide' : 'show';
},
// ... 其他派生状态
},
actions: {
toggleVisibility() {
state.isVisible = ! state.isVisible;
},
// ... 其他操作
},
} );
```
在这段代码中:
- `isVisible`和`isActive`是基础状态属性
- `visibilityText`是派生状态,会在`isVisible`变更时自动更新
- `toggleVisibility`是修改状态的操作
HTML绑定如下所示
```html
<button
data-wp-on--click="actions.toggleVisibility"
data-wp-text="state.visibilityText"
data-wp-bind--aria-expanded="state.isVisible"
>
show
</button>
```
响应式机制的实际运作流程:
1. 当按钮被点击时,触发`toggleVisibility`操作
2. 该操作更新`state.isVisible`
3. 交互式API检测到此变更并自动
- 更新按钮的文本内容(因为`data-wp-text="state.visibilityText"`
- 更改`aria-expanded`属性(由于`data-wp-bind--aria-expanded="state.isVisible"`
- 更新任何其他依赖`isVisible`或`visibilityText`的DOM部分
# 响应式与声明式思维模式
交互性API是一个响应式声明式框架与其他现代框架如React、Vue、Svelte或Alpine类似。在使用交互性API时采用正确的思维模式对于充分发挥其潜力至关重要。本指南将解释响应式和声明式的核心概念为有效使用交互性API奠定基础。
## 声明式 vs 命令式
**声明式编程**描述的是程序_应该实现什么_它关注期望的结果而不显式列出实现该结果的命令或步骤。相比之下**命令式编程**通过明确说明操作程序状态的每个步骤来指定_如何_完成任务。
### 命令式方法
在Web开发的早期阶段命令式方法占据主导地位。这种方法涉及使用JavaScript手动更新DOM以反映变化。
以这个包含两个按钮和一个段落的交互式区块为例:
- **显示/隐藏按钮**:切换段落可见性并启用/禁用"激活"按钮
- **激活/停用按钮**:在"激活"(绿色)和"非激活"(红色)状态间切换段落文本和颜色
```html
<div id="my-interactive-plugin">
<button
id="show-hide-btn"
aria-expanded="false"
aria-controls="status-paragraph"
>
显示
</button>
<button id="activate-btn" disabled>激活</button>
<p id="status-paragraph" class="inactive" hidden>这是非激活状态</p>
</div>
<style>
.active {
color: green;
}
.inactive {
color: red;
}
</style>
<script>
const showHideBtn = document.getElementById( 'show-hide-btn' );
const activateBtn = document.getElementById( 'activate-btn' );
const statusParagraph = document.getElementById( 'status-paragraph' );
showHideBtn.addEventListener( 'click', () => {
if ( statusParagraph.hasAttribute( 'hidden' ) ) {
statusParagraph.removeAttribute( 'hidden' );
showHideBtn.textContent = '隐藏';
showHideBtn.setAttribute( 'aria-expanded', 'true' );
activateBtn.removeAttribute( 'disabled' );
} else {
if ( statusParagraph.classList.contains( 'active' ) ) {
statusParagraph.textContent = '这是非激活状态';
statusParagraph.classList.remove( 'active' );
activateBtn.textContent = '激活';
}
statusParagraph.setAttribute( 'hidden', true );
showHideBtn.textContent = '显示';
showHideBtn.setAttribute( 'aria-expanded', 'false' );
activateBtn.setAttribute( 'disabled', true );
}
} );
activateBtn.addEventListener( 'click', () => {
if ( activateBtn.textContent === '激活' ) {
statusParagraph.textContent = '这是激活状态';
statusParagraph.classList.remove( 'inactive' );
statusParagraph.classList.add( 'active' );
activateBtn.textContent = '停用';
} else {
statusParagraph.textContent = '这是非激活状态';
statusParagraph.classList.remove( 'active' );
statusParagraph.classList.add( 'inactive' );
activateBtn.textContent = '激活';
}
} );
</script>
```
如您所见对于每种情况您都必须使用JavaScript来修改DOM中所有已更改的内容同时还需要考虑之前的状态。
### 声明式方法
声明式方法通过关注_应该发生什么_来简化流程。用户界面会根据状态变化自动更新。以下是使用交互性API声明式方法的类似示例
```html
<div id="my-interactive-plugin" data-wp-interactive="myInteractivePlugin">
<button
data-wp-on--click="actions.toggleVisibility"
data-wp-bind--aria-expanded="state.isVisible"
data-wp-text="state.visibilityText"
aria-controls="status-paragraph"
>
显示
</button>
<button
data-wp-on--click="actions.toggleActivation"
data-wp-bind--disabled="!state.isVisible"
data-wp-text="state.activationText"
>
激活
</button>
<p
id="status-paragraph"
data-wp-bind--hidden="!state.isVisible"
data-wp-class--active="state.isActive"
data-wp-class--inactive="!state.isActive"
data-wp-text="state.paragraphText"
>
这是非激活状态
</p>
</div>
<style>
.active {
color: green;
}
.inactive {
color: red;
}
</style>
```
```js
import { store } from '@wordpress/interactivity';
const { state } = store( 'myInteractivePlugin', {
state: {
isVisible: false,
isActive: false,
get visibilityText() {
return state.isVisible ? '隐藏' : '显示';
},
get activationText() {
return state.isActive ? '停用' : '激活';
},
get paragraphText() {
return state.isActive ? '这是激活状态' : '这是非激活状态';
},
},
actions: {
toggleVisibility() {
state.isVisible = ! state.isVisible;
if ( ! state.isVisible ) state.isActive = false;
},
toggleActivation() {
state.isActive = ! state.isActive;
},
},
} );
```
在这个声明式示例中用户界面会根据当前状态自动更新。作为开发人员您只需要声明必要的状态、任何派生状态、修改状态的操作以及DOM的哪些部分依赖于状态的哪些部分。框架会负责对DOM进行所有必要的更新使其始终与当前状态保持同步。无论框架控制的元素数量有多少逻辑都保持简单且易于维护。