Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Общее состояние: useState

Иногда одни и те же данные нужны в разных частях сайта: имя вошедшего пользователя показывается и в шапке, и на странице профиля. useState создаёт такое общее значение по ключу. Все компоненты, которые обращаются к одному ключу, получают один и тот же реактивный ref.

Минимальный пример

<!-- app/pages/index.vue -->
<script setup lang="ts">
const visits = useState('visits', () => 0)
</script>
 
<template>
  <section>
    <h2>Главная</h2>
    <button @click="visits++">Нажато: {{ visits }}</button>
  </section>
</template>

Первый аргумент — ключ, второй — функция, которая задаёт начальное значение. Нажмите кнопку несколько раз, перейдите на /about и вернитесь — счётчик сохранит значение, ведь он хранится вне компонента.

Composable с общим состоянием

Ключ удобно спрятать в composable, чтобы не повторять его в каждом файле. Создайте app/composables/useCurrentUser.ts:

// app/composables/useCurrentUser.ts
export function useCurrentUser() {
  return useState<string | null>('current-user', () => null)
}

Тип в угловых скобках говорит, что значение — строка или null, если никто не вошёл.

Использование в двух местах

Шапка в макете показывает имя:

<!-- app/layouts/default.vue -->
<script setup lang="ts">
const user = useCurrentUser()
</script>
 
<template>
  <div>
    <header>
      <NuxtLink to="/">Главная</NuxtLink>
      <span v-if="user">Привет, {{ user }}</span>
      <NuxtLink v-else to="/login">Войти</NuxtLink>
    </header>
    <slot />
  </div>
</template>

А страница входа его меняет:

<!-- app/pages/login.vue -->
<script setup lang="ts">
const user = useCurrentUser()
const name = ref('')
 
function login() {
  user.value = name.value
  navigateTo('/')
}
</script>
 
<template>
  <form @submit.prevent="login">
    <input v-model="name" placeholder="Ваше имя">
    <button>Войти</button>
  </form>
</template>

Введите на /login имя «Анна» и нажмите «Войти»: в шапке появится «Привет, Анна». Это пока не настоящий вход — после перезагрузки страницы имя пропадёт. Вход с проверкой пароля разобран в уроке об аутентификации.

Почему не обычный ref

СпособЧто происходит
ref внутри компонентаСвоё значение у каждого компонента
ref в файле вне функцииНа сервере одно значение на всех посетителей
useStateОтдельное значение для каждого запроса, переходит с сервера в браузер

Значение, заданное на сервере, Nuxt передаёт в браузер вместе с HTML, поэтому после гидратации оно не теряется.

Частая ошибка

Объявить общее состояние так: export const user = ref(null) в отдельном файле. В браузере это работает, но на сервере такой ref один на всех посетителей сразу: имя, сохранённое во время запроса Анны, увидит и Олег. Для общих данных используйте useState или Pinia. И храните в useState только простые данные — строки, числа, массивы и объекты, но не функции и не экземпляры классов: их нельзя передать в браузер.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.