Day11 - Astro Series: Global state management

Astro 系列文第十一日:全局状态管理

一个漂亮的渐变背景上有一句标题:“全局状态管理”

前言

之前章节了解到在 Astro 中引用不同框架的组件非常容易,但这些组件的状态如何管理?让我们进入全局状态管理的世界。

在不同组件之间传递状态

在一些 UI 框架中会提供创建 context 的方式来管理状态,但由于 Astro 采用「局部 Hydration」的方式来渲染页面,因此无法做到。

因此 Astro 推荐的解方是:Nano Stores🔗

为什么选择 Nano Stores?

  • 它很小(不到 1 KB)。
  • 与框架无关,可用于整合不同框架组件之间的状态。

什么?又要学一个新的框架?根据我简单的体验,它与其他状态管理并没有太大差异,只需要对 JavaScript 有基础的了解就能很快上手。以下是一个小示例,展示如何在 Vue 与 React 之间传递和管理状态。

计数器小示例

定义问题

目前有两个计数器组件: CounterVue.vueCounterReact.jsx 希望通过导入 Nano Stores 让这两个组件之间的状态可以互通。

第一步:安装对应套件

Terminal window
npx astro add react
npx astro add vue
npm install nanostores @nanostores/react
npm install nanostores @nanostores/vue

第一步:创建 Store

创建一个 stores 文件夹并且添加 counter.js 文件,透过 atom 这个函数的帮助,我们能将状态记录于其中并且导出以在其他组件中引用。

counter.js
import { atom } from 'nanostores';
const counter = atom({ value: 1 });
const increaseCounter = () => counter.set({ value: counter.get().value + 1 });
const decreaseCounter = () => {
counter.set({ value: counter.get().value - 1 });
};
export { counter, increaseCounter, decreaseCounter };

第二步:撰写个别元件

CounterReact.jsx
import { useStore } from '@nanostores/react';
import { counter, increaseCounter, decreaseCounter } from '../stores/counter';
export default function CounterReact() {
const count = useStore(counter);
return (
<div style={{ border: '1px solid blue' }}>
<button onClick={decreaseCounter}>-</button>
<div>{count.value}</div>
<button onClick={increaseCounter}>+</button>
<p>React Component</p>
</div>
);
}
CounterVue.vue
<script setup>
import { useStore } from '@nanostores/vue';
import { counter, increaseCounter, decreaseCounter } from '../stores/counter';
const count = useStore(counter);
</script>
<template>
<div style="border: 1px solid green">
<button @click="decreaseCounter">-</button>
<div>{{ count.value }}</div>
<button @click="increaseCounter">+</button>
<p>Vue Component</p>
</div>
</template>

第三步:引入元件

---
import CounterReact from '../components/CounterReact';
import CounterVue from '../components/CounterVue.vue';
---
<div class="container">
<CounterReact client:load />
<CounterVue client:load />
</div>
<style>
.container {
display: flex;
padding: 2rem;
gap: 1rem;
}
</style>

一个 Vue 计数器与 React 计数器在同个页面上

这样一来两种不同框架的状态与方法也能很好地配合在一起。

其他替代方案?

可以考虑一些“简单”的方法,比如通过「自定义浏览器事件🔗」让组件之间彼此沟通,或是依赖各框架提供的解决方案:

总结

今天的示例展示了在 Astro 中如何处理不同框架间的状态。在以静态内容为主的网站中,通常也较少需要写到复杂的客户端状态管理,很高兴有套件可以简单整合这件事情。

延伸阅读