Astro 中的 상태 관리: 纳米店铺 深 dive

‘Nanostores in Astro: A Multi-Framework Adventure’ 시리즈의 첫 부분에 오신 것을 환영합니다. 다양한 框架에서 Astro 프로젝트의 상태 관리에 고민 겹쳤다면, 좋은 것을 기대하세요. 오늘, Nanostores라는 featherweight 상태 관리 솔루션에 대해 살펴봐요. 이 솔루션은 Astro의 component islands 아키텍처와 无缝으로 통합할 수 있습니다.

이 글에서는 Nanostores가 Astro, React, Vue, Svelte components 사이에서 어떻게 상태 관리를 스트리밍할 수 있는지 자세히 다룰 것입니다. 독립적인 상태, 공유된 상태에 대해서 알아보고, 영구적인 상태 관리를 introduce 할 것입니다. Astro 프로젝트의 상태 관리를 简素화 시키는 여행을 시작해봅시다.

right away 하고 싶다면, 간단한 요약과 기본적인 링크를 확인할 수 있습니다:

이 記事 旁边에서 시험과 코드를 함께 探rip하여 实务 기능을 습득할 수 있습니다!

Nanostores는 프레임워크 독립성을 염두에 두고 설계된 미니멀한 상태 관리 라이브러리입니다. 애플리케이션의 다양한 부분에서 쉽게 공유하고 업데이트할 수 있는 원자적 상태 조각을 생성하고 관리할 수 있는 간단한 API를 제공합니다.

  1. 경량 및 빠름: Nanostores는 매우 작습니다 (265에서 814바이트 사이)로, 번들 크기를 부풀리지 않습니다.

  2. 프레임워크 독립적: Astro의 다중 프레임워크 생태계에 완벽합니다. React, Vue, Svelte, Solid 및 순수 JavaScript와 매끄럽게 통합됩니다.

  3. 간단한 API: 복잡한 설정이나 보일러플레이트가 없습니다. 사용하기에 직관적이고 간단합니다.

  4. Astro의 컴포넌트 아일랜드에 보완적: Nanostores는 Astro의 아일랜드 아키텍처를 강화하여 고립된 인터랙티브 컴포넌트 간 효율적인 상태 관리를 가능하게 합니다.

Nanostores는 세 가지 주요 개념을 사圉에 두고 있습니다.

  1. Atoms: 단일 값을 보관하는 간단한 스토어입니다.

  2. Maps: 다양한 속성을 가진 오브젝트를 보관하는 스토어입니다.

  3. Computed Stores: 다른 스토어의 값에 따라 값을 계산하는 派生 스토어입니다.

빠른 예제를 보겠습니다:

import { atom, map, computed } from 'nanostores'

// Atom
const count = atom(0)

// Map
const user = map({ name: 'Astro Fan', isLoggedIn: false })

// Computed Store
const greeting = computed([user], (user) => 
  user.isLoggedIn ? `Welcome back, ${user.name}!` : 'Hello, guest!'
)

이 스니펫(snippet)에서는 카운터를 위한 아том(atom), 사용자 데이터를 위한 맵(map), 동적인 인사를 위한 computed store를 만듭니다. 간단합니다, correct?

Astro 프로젝트에 Nanostores를 시작하는 것은 간단합니다. 이러한 방법으로 시작하세요:

  1. 처음으로 Nanostores와 그 프레임웍(framework integrations)을 설치합니다:
# Using npm
npm install nanostores
npm install @nanostores/react  # For React
npm install @nanostores/vue    # For Vue

# Using yarn
yarn add nanostores
yarn add @nanostores/react     # For React
yarn add @nanostores/vue       # For Vue

# Using pnpm
pnpm add nanostores
pnpm add @nanostores/react     # For React
pnpm add @nanostores/vue       # For Vue

# Note: Svelte doesn't require a separate integration
  1. 스토어의 새 파일을 생성하ましょう, src/stores/counterStore.js라고 합니다:
import { atom } from 'nanostores'

export const count = atom(0)

export function increment() {
  count.set(count.get() + 1)
}

export function decrement() {
  count.set(count.get() - 1)
}
  1. 이제 이 스토어를 components 的任何에서 사용할 수 있습니다. 아래 예제와 같이 Astro component에서 빠른 example를 보여드릴 수 있습니다:
---
import { count, increment, decrement } from '../stores/counterStore'
---

<div>
  <button onclick={decrement}>-</button>
  <span>{count.get()}</span>
  <button onclick={increment}>+</button>
</div>

<script>
  import { count } from '../stores/counterStore'

  count.subscribe(value => {
    document.querySelector('span').textContent = value
  })
</script>

그럼 이제! 您 just Astro project에서 Nanostore를 설정했습니다.

다양한 프레임워크를 사용하는 Astro 프로젝트에서, 다른 프레임워크의 컴포넌트 내에서 독립적으로 상태를 관리하고자 할 수 있습니다. Nanostores는 이를 无缝하게 実現합니다. React, Vue, Svelte, 및 Astro 컴포넌트 之间에서 독립적인 상태 관리를 어떻게 구현하는지 예시를 参照하세요.

각 프레임워크에서 간단한 Counter를 구현하여 독립적인 상태 관리를 보여드리ます.

まず, 독립적인 Counter 스토어를 생성하겠습니다:

// src/stores/independentCounterStore.js
import { atom } from 'nanostores'

export const reactCount = atom(0)
export const vueCount = atom(0)
export const svelteCount = atom(0)
export const astroCount = atom(0)

export function increment(store) {
  store.set(store.get() + 1)
}

export function decrement(store) {
  store.set(store.get() - 1)
}

次に、각 프레임ework에서 이 카운터를 구현하겠습니다:

// src/components/ReactCounter.jsx
import { useStore } from '@nanostores/react'
import { reactCount, increment, decrement } from '../stores/independentCounterStore'

export function ReactCounter() {
  const count = useStore(reactCount)

  return (
    <div>
      <button onClick={() => decrement(reactCount)}>-</button>
      <span>{count}</span>
      <button onClick={() => increment(reactCount)}>+</button>
    </div>
  )
}
<!-- src/components/VueCounter.vue -->
<template>
  <div>
    <button @click="decrement(vueCount)">-</button>
    <span>{{ count }}</span>
    <button @click="increment(vueCount)">+</button>
  </div>
</template>

<script setup>
import { useStore } from '@nanostores/vue'
import { vueCount, increment, decrement } from '../stores/independentCounterStore'

const count = useStore(vueCount)
</script>
<!-- src/components/SvelteCounter.svelte -->
<script>
import { svelteCount, increment, decrement } from '../stores/independentCounterStore'
</script>

<div>
  <button on:click={() => decrement(svelteCount)}>-</button>
  <span>{$svelteCount}</span>
  <button on:click={() => increment(svelteCount)}>+</button>
</div>
---
import { astroCount, increment, decrement } from '../stores/independentCounterStore'
---

<div>
  <button id="decrement">-</button>
  <span id="count">{astroCount.get()}</span>
  <button id="increment">+</button>
</div>

<script>
  import { astroCount, increment, decrement } from '../stores/independentCounterStore'

  document.getElementById('decrement').addEventListener('click', () => decrement(astroCount))
  document.getElementById('increment').addEventListener('click', () => increment(astroCount))

  astroCount.subscribe(value => {
    document.getElementById('count').textContent = value
  })
</script>

正如你所见,각 프레임ework 컴포넌트는 Nanostores를 사용하여 자신만의 독립적인 Counter 상태를 유지합니다. 이러한 접근法은 사용하는 프레임ework에 상관없이 각 컴포넌트 내에서 isolate된 상태 관리를 허용합니다.

Nanostores가 다양한 프레임ework 컴포넌트 사이에서 공유된 상태를 어떻게 가능하게 하는지 이제 예시로 살펴봅니다. 이러한 방법은 你的 응용 프로그램의 다양한 부분之间 상태를 동기화하는 데 유용합니다.

React, Vue, Svelte, 및 Astro 컴포넌트 之间에서 업데이트하고 표시할 수 있는 공유된 Counter를 생성하겠습니다.

まず, 공유 Counter 스토어를 생성하겠습니다:

// src/stores/sharedCounterStore.js
import { atom } from 'nanostores'

export const sharedCount = atom(0)

export function increment() {
  sharedCount.set(sharedCount.get() + 1)
}

export function decrement() {
  sharedCount.set(sharedCount.get() - 1)
}

次に、이 공유 상태를 사용하는 각 프레임ework의 컴포넌트를 구현하겠습니다:

// src/components/ReactSharedCounter.jsx
import { useStore } from '@nanostores/react'
import { sharedCount, increment, decrement } from '../stores/sharedCounterStore'

export function ReactSharedCounter() {
  const count = useStore(sharedCount)

  return (
    <div>
      <h2>React Shared Counter</h2>
      <button onClick={decrement}>-</button>
      <span>{count}</span>
      <button onClick={increment}>+</button>
    </div>
  )
}
<!-- src/components/VueSharedCounter.vue -->
<template>
  <div>
    <h2>Vue Shared Counter</h2>
    <button @click="decrement">-</button>
    <span>{{ count }}</span>
    <button @click="increment">+</button>
  </div>
</template>

<script setup>
import { useStore } from '@nanostores/vue'
import { sharedCount, increment, decrement } from '../stores/sharedCounterStore'

const count = useStore(sharedCount)
</script>
<!-- src/components/SvelteSharedCounter.svelte -->
<script>
import { sharedCount, increment, decrement } from '../stores/sharedCounterStore'
</script>

<div>
  <h2>Svelte Shared Counter</h2>
  <button on:click={decrement}>-</button>
  <span>{$sharedCount}</span>
  <button on:click={increment}>+</button>
</div>
---
import { sharedCount, increment, decrement } from '../stores/sharedCounterStore'
---

<div>
  <h2>Astro Shared Counter</h2>
  <button id="shared-decrement">-</button>
  <span id="shared-count">{sharedCount.get()}</span>
  <button id="shared-increment">+</button>
</div>

<script>
  import { sharedCount, increment, decrement } from '../stores/sharedCounterStore'

  document.getElementById('shared-decrement').addEventListener('click', decrement)
  document.getElementById('shared-increment').addEventListener('click', increment)

  sharedCount.subscribe(value => {
    document.getElementById('shared-count').textContent = value
  })
</script>

이 셋업으로 모든 이러한 组件들이 同一个 计数器 状态을 공유할 것입니다. 어느 组件에서 计数器를 증가 또는 감소 시키면, 사용하는 框架이 무엇이든 모두 components에서 값을 갱신할 것입니다.

독립적이고 공유된 状態는 강력하지만, 状態가 页面 새로고침 또는 브라우저 세션 之间에서 持続되어야 할 때가 있습니다. 이러한 경우 @nanostores/persistent 가 사용되는 것입니다. Astro 项目中如何实现持続적인 状態를 exploratory.

まず, Nanostores의 持続적인 添加剂를 安装해야 합니다:

# Using npm
npm install @nanostores/persistent

# Using yarn
yarn add @nanostores/persistent

# Using pnpm
pnpm add @nanostores/persistent

이제, 页面 새로고침 시 값을 유지하는 持続적인 计数器을 생성해봅시다:

// src/stores/persistentCounterStore.js
import { persistentAtom } from '@nanostores/persistent'

export const persistentCount = persistentAtom('persistentCount', 0)

export function increment() {
  persistentCount.set(persistentCount.get() + 1)
}

export function decrement() {
  persistentCount.set(persistentCount.get() - 1)
}

export function reset() {
  persistentCount.set(0)
}

이 예에서 ‘persistentCount’는 localStorage에 값을 저장할 때 사용하는 키이고, 0은 초기 값입니다.

다양한 框架의 组件로 持続적인 计数器를 구현해봅시다. 이 计数器는 页面 새로고침 시 값을 유지하며 어느 框架에서도 접근할 수 있습니다.

// src/components/ReactPersistentIncrement.jsx
import { useStore } from '@nanostores/react'
import { persistentCount, increment } from '../stores/persistentCounterStore'

export function ReactPersistentIncrement() {
  const count = useStore(persistentCount)

  return (
    <button onClick={increment}>
      React Increment: {count}
    </button>
  )
}
<!-- src/components/VuePersistentDecrement.vue -->
<template>
  <button @click="decrement">
    Vue Decrement: {{ count }}
  </button>
</template>

<script setup>
import { useStore } from '@nanostores/vue'
import { persistentCount, decrement } from '../stores/persistentCounterStore'

const count = useStore(persistentCount)
</script>
<!-- src/components/SveltePersistentDisplay.svelte -->
<script>
import { persistentCount } from '../stores/persistentCounterStore'
</script>

<div>
  Svelte Display: {$persistentCount}
</div>
---
import { reset } from '../stores/persistentCounterStore'
---

<button id="reset-button">Astro Reset</button>

<script>
  import { persistentCount, reset } from '../stores/persistentCounterStore'

  document.getElementById('reset-button').addEventListener('click', reset)

  persistentCount.subscribe(value => {
    console.log('Persistent count updated:', value)
  })
</script>

이제, Astro 页面에 이러한 组件들을 함께 사용할 수 있습니다:

---
import ReactPersistentIncrement from '../components/ReactPersistentIncrement'
import VuePersistentDecrement from '../components/VuePersistentDecrement.vue'
import SveltePersistentDisplay from '../components/SveltePersistentDisplay.svelte'
---

<div>
  <h2>Persistent Counter Across Frameworks</h2>
  <ReactPersistentIncrement client:load />
  <VuePersistentDecrement client:load />
  <SveltePersistentDisplay client:load />
  <button id="reset-button">Astro Reset</button>
</div>

<script>
  import { persistentCount, reset } from '../stores/persistentCounterStore'

  document.getElementById('reset-button').addEventListener('click', reset)

  persistentCount.subscribe(value => {
    console.log('Persistent count updated:', value)
  })
</script>

이 셋업은 다음과 같은 持続적인 计数器를 보여줍니다.:

  • React가 증가를 처리한다

  • Vue가 감소를 처리한다

  • Svelte는 현재 카운트를 표시합니다

  • Astro는 리셋 버튼을 제공합니다

카운터의 값은 페이지를 새로고침해도 유지되며, 상태를 유지하는 데 있어 @nanostores/persistent의 강력함을 보여줍니다.

영구 상태는 다음과 같은 경우에 특히 유용합니다:

  1. 사용자 선호 설정 (예: 테마 설정, 언어 선택)

  2. 부분적으로 완료된 양식 (실수로 페이지를 새로고침할 때 데이터 손실 방지)

  3. 인증 토큰 (사용자 세션 유지용)

  4. 자주 접근하는 데이터의 로컬 캐시

@nanostores/persistent를 활용하면 중요한 상태 데이터를 페이지 로드 및 브라우저 세션 동안 유지하여 사용자 경험을 향상시킬 수 있습니다.

Astro 프로젝트에 Nanostores를 통합할 때 이 경량 상태 관리 솔루션을 최대한 활용하기 위해 다음의 최선의 방법 및 팁을 염두에 두십시오.

  • 간단한 단일 값 상태에는 atom을 사용하십시오.

  • 여러 속성을 가진 객체와 같은 상태에는 map을 사용하세요.

  • 다른 스토어에 의존하는 파생 상태에는 computed을 사용하세요.

  • 페이지 새로 고침 후에도 상태를 유지하려면 persistentAtom 또는 persistentMap을 사용하세요.

큰 모놀리식 스토어를 만드는 대신, 더 작고 집중된 스토어를 선호하세요. 이 방법은 유지보수성과 성능을 향상시키며, 더 세분화된 업데이트가 가능하게 합니다.

// Prefer this:
const userProfile = map({ name: '', email: '' })
const userPreferences = map({ theme: 'light', language: 'en' })

// Over this:
const user = map({ name: '', email: '', theme: 'light', language: 'en' })

다른 상태에 의존하는 상태가 있을 때는 계산된 스토어를 사용하세요. 이렇게 하면 상태가 중복되지 않고, 파생 상태가 항상 최신 상태를 유지하게 됩니다.

import { atom, computed } from 'nanostores'

const firstName = atom('John')
const lastName = atom('Doe')

const fullName = computed(
  [firstName, lastName],
  (first, last) => `${first} ${last}`
)

Nanostores는 훌륭한 TypeScript 지원을 제공합니다. 이를 사용하여 오류를 조기에 발견하고 개발자 경험을 향상시키세요.

import { atom } from 'nanostores'

interface User {
  id: number
  name: string
}

const currentUser = atom<User | null>(null)

Nanostores는 경량이지만, 대규모 애플리케이션에서 성능을 염두에 두세요. 여러 스토어 업데이트를 그룹화하여 다시 렌더링 수를 줄이기 위해 batched 함수를 사용하세요.

import { atom, batched } from 'nanostores'

const count1 = atom(0)
const count2 = atom(0)

export const incrementBoth = batched(() => {
  count1.set(count1.get() + 1)
  count2.set(count2.get() + 1)
})

다중 프레임워크 Astro 프로젝트에서 Nanostores를 사용할 때, 핵심 상태 로직은 프레임워크에 종속되지 않도록 유지하세요. 이렇게 하면 다양한 프레임워크 구성 요소 간에 상태를 공유하기가 더 쉬워집니다.

// stores/themeStore.js
import { atom } from 'nanostores'

export const theme = atom('light')

export function toggleTheme() {
  theme.set(theme.get() === 'light' ? 'dark' : 'light')
}

// React component
import { useStore } from '@nanostores/react'
import { theme, toggleTheme } from '../stores/themeStore'

function ThemeToggle() {
  const currentTheme = useStore(theme)
  return <button onClick={toggleTheme}>{currentTheme}</button>
}

영구 저장소는 강력하지만 신중하게 사용해야 합니다. 모든 상태가 세션 간에 지속될 필요는 없습니다. 영구 저장소를 과도하게 사용하면 예상치 못한 동작과 성능 문제를 일으킬 수 있습니다.

디버깅을 쉽게 하기 위해, onMount 함수를 사용하여 상태 변화를 로그로 남길 수 있습니다:

import { atom, onMount } from 'nanostores'

const count = atom(0)

if (import.meta.env.DEV) {
  onMount(count, () => {
    count.listen((value) => {
      console.log('Count changed:', value)
    })
  })
}

언마운트될 수 있는 구성 요소에서 Nanostores를 사용할 때, 메모리 누수를 방지하기 위해 구독을 정리하는 것을 잊지 마세요.

import { useEffect } from 'react'
import { count } from '../stores/countStore'

function Counter() {
  useEffect(() => {
    const unsubscribe = count.subscribe(() => {
      // Do something
    })
    return unsubscribe
  }, [])

  // Rest of the component
}

이러한 모범 사례와 팁을 따르면, 어떤 프레임워크를 통합하든지 상관없이 Nanostores를 사용하여 Astro 프로젝트에서 상태를 효과적으로 관리할 수 있습니다.

이 기사에서 다룬 바와 같이, Nanostores는 특히 여러 프레임워크와 작업할 때 Astro 프로젝트에서 상태 관리를 위한 강력하면서도 가벼운 솔루션을 제공합니다. 주요 요점을 다시 요약해 보겠습니다:

  1. 다재다능성: Nanostores는 Astro, React, Vue, Svelte 및 Solid와 원활하게 통합되어 다중 프레임워크 프로젝트에 이상적인 선택입니다.

  2. 간단함

    : 간단한 API로 纳米店(Nanostores)은 학습 曲线上(learning curve)을 낮게 해줍니다. 그러나 동시에 강한 状態 관리 기능을 제공합니다.

  3. 유연성: 纳米店(Nanostores)는 간단한 atom 状態 Store에서 복잡한 계산 状態에 이르는 것까지, 甚至是 持続적인 저장을 포함한 다양한 状態 관리 needs에 적응합니다.
  4. 성능: 纳米店(Nanostores)의 featheright 性质은 당신의 응용 프로그램을 Lent 하지 않기 때문에 가능하며, Astro의 성능 이득을 유지합니다.
  5. 最优惯例: 纳米店(Nanostores)의 당신이 讨论 한 가이elines를 따라야 합니다. 예를 들어 状态 Store를 작게 유지하고 焦点를 맞추고, TypeScript을 사용하고, 派生 状态를 위한 계산 状态 Store를 사용하는 것입니다. 이러한 指导方针을 따라하면 유지 가능하고 효율적인 状態 관리 시스템을 만들 수 있습니다.

Nanostores는 Astro의 컴포넌트 아일랜드 아키텍처에서 빛을 발하며, 고립된 인터랙티브 컴포넌트 간의 상태를 효율적으로 관리할 수 있게 해줍니다. 몇 가지 인터랙티브 요소가 있는 간단한 웹사이트를 구축하든, 여러 프레임워크가 있는 복잡한 웹 애플리케이션을 구축하든, Nanostores는 상태를 효과적으로 처리할 수 있는 도구를 제공합니다.

Astro와 Nanostores와 함께하는 여정을 계속하면서, 가장 좋은 학습 방법은 직접 해보는 것임을 기억하세요. 다양한 스토어 유형을 실험하고, 프레임워크 간의 공유 상태를 구현해 보며, 지속적 저장소의 가능성을 탐구해 보세요. 각 프로젝트는 상태 관리 기술을 연마할 새로운 도전과 기회를 제공할 것입니다.

우리의 “Astro에서의 Nanostores: 다중 프레임워크 모험” 시리즈의 다음 기사에서 Astro 프로젝트에서 상태 관리를 위한 실용적인 응용 프로그램과 고급 기술에 대해 더 깊이 다룰 예정이니 계속 지켜봐 주세요.

Astro 프로젝트에서 Nanostores의 사용법에 대한 이해를 심화하려면 다음의 귀중한 자료들을 확인해 보세요:

멋진 코딩하세요, ваши Astro 프로젝ты는 영구적이고 효율적으로 동작하続けます!

Source:
https://meirjc.hashnode.dev/state-management-in-astro-a-deep-dive-into-nanostores