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