### 可变性与不可变性

与许多其他响应式框架不同，**交互式 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进行所有必要的更新，使其始终与当前状态保持同步。无论框架控制的元素数量有多少，逻辑都保持简单且易于维护。