По мере того, как вы создаете приложения Vue.js и они начинают достигать определенного размера, вы, вероятно, столкнетесь с необходимостью глобального управления состоянием. Удобно, что основная группа разработчиков предоставляет Vuex, де-факто библиотеку управления состоянием для приложений Vue.js.

Начать работу довольно просто, и я предполагаю, что вы уже знакомы с реализацией Vuex. В конце концов, этот пост не о том, чтобы начать работу. Если вам это нужно, то я бы порекомендовал заглянуть в документацию.

Vuex значительно упрощает управление глобальным хранилищем данных, и для следующих примеров предположим, что у нас есть хранилище, которое выглядит примерно так:

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
  state: {
    user: null
  },
  mutations: {
    setUser (state, user) {
      state.user = user
    }
  },
})

Состояние магазина начинается с пустого user объекта и setUser мутации, которая может обновлять состояние. Затем в нашем приложении мы можем захотеть показать данные пользователя:

<template>
  <div>
    <p v-if="user">Hi {{ user.name }}, welcome back!</p>
    <p v-else>You should probably log in.</p>
  </div>
</template>
<script>
export default {
  computed {
    user() {
      return this.$store.state.user
    }
  }
}
</script>

Итак, когда приложение загружается, оно показывает пользователю приветственное сообщение, если он вошел в систему. В противном случае он сообщает им, что им нужно войти в систему. Я знаю, что это тривиальный пример, но, надеюсь, вы столкнулись с чем-то похожим на это.

Если вы похожи на меня, возникает вопрос:

Как добавить данные в магазин до загрузки приложения?

Что ж, есть несколько вариантов.

Установить начальное состояние

Самый наивный подход к предварительному заполнению вашего глобального магазина - установить начальное состояние при создании вашего магазина:

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
  state: {
    user: { name: "Austin" }
  },
  mutations: {
    setUser (user) {
      state.user = user
    }
  }
})

Очевидно, это работает только в том случае, если вы заранее знаете подробности о пользователе. Когда мы создаем приложение, мы, вероятно, не узнаем имя пользователя, но есть еще один вариант.

Однако мы можем воспользоваться localStorage, чтобы сохранить копию информации пользователя. Когда они входят в систему, вы устанавливаете данные в localStorage, а когда они входят в наш, вы удаляете данные из localStorage.

Когда приложение загружается, вы можете вытащить данные пользователя из localStorage в исходное состояние:

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
  state: {
    user: localStorage.get('user')
  },
  mutations: {
    setUser (user) {
      state.user = user
    }
  }
})

Если вы работаете с данными, для которых не требуются сверхжесткие ограничения безопасности, это работает очень хорошо. Я бы порекомендовал vuex-persistedstate библиотеку, чтобы помочь автоматизировать это.

Имейте в виду, что вы никогда не должны хранить очень конфиденциальные данные, такие как токены аутентификации, в localStorage, потому что они могут стать целью XSS-атак. Итак, наш пример подходит для имени пользователя, но не для чего-то вроде токена авторизации. Они должны храниться только в памяти (которая все еще может быть Vuex, но только не сохраняться).

Запрос данных при установке приложения

Теперь предположим, по какой причине мы не хотим хранить данные в localStorage. Следующим вариантом может быть оставление исходного состояния пустым и разрешение на монтирование нашего приложения. После того, как приложение смонтировано, мы можем сделать HTTP-запрос к нашему серверу, чтобы получить наши данные, а затем обновить глобальное состояние:

<template>
  <div>
    <p v-if="user">Hi {{ user.name }}, welcome back!</p>
    <p v-else>You should probably log in.</p>
  </div>
</template>
<script>
export default {
  computed {
    user() {
      return this.$store.state.user
    }
  },
  async mounted() {
    const user = await getUser() // Assume getUser returns a user object with a name property
    this.$store.commit('setUser', user)
  }
}
</script>

Это нормально работает, но теперь у нас странный пользовательский опыт. Приложение загрузится и отправит запрос, но пока пользователь ожидает возврата запроса, он видит сообщение «Вероятно, вам следует войти в систему». Когда запрос возвращается, при условии, что они вошли в сеанс, это сообщение быстро меняется на «Привет {{ user.name }}, добро пожаловать обратно!». Эта вспышка может выглядеть дрянной.

Чтобы исправить эту вспышку, мы можем просто показать элемент загрузки, пока запрос отсутствует:

<template>
  <div>
    <p v-if="loading">Loading...</p>
    <p v-else-if="user">Hi {{ user.name }}, welcome back!</p>
    <p v-else>You should probably log in.</p>
  </div>
</template>
<script>
export default {
  data: () => ({
    loading: false
  }),
  computed {
    user() {
      return this.$store.state.user
    }
  },
  async mounted() {
    this.loading = true
    const user = await fetch('/user').then(r => r.json()) // Assume getUser returns a user object with a name property
    this.$store.commit('setUser', user)
    this.loading = false
  }
}
</script>

Имейте в виду, что это очень простой пример. В вашем у вас может быть специальный компонент для загрузки анимации, и у вас может быть компонент <router-view> вместо пользовательских сообщений здесь. Вы также можете сделать этот HTTP-запрос из действия Vuex. Концепция все еще применяется.

Запрос данных перед загрузкой приложения

Последний пример, который я рассмотрю, - это выполнение HTTP-запросов, аналогичных предыдущему, но ожидание возврата запроса и обновление хранилища до того, как приложение когда-либо сможет загрузиться.

Если мы помним, что хранилище Vuex - это просто объект с некоторыми свойствами и методами, мы можем обращаться с ним так же, как с любым другим объектом JavaScript.

Мы можем импортировать наше хранилище в наш main.js файл (или любую другую точку входа для вашего приложения) и вызвать наш HTTP-запрос перед монтированием приложения:

import Vue from "vue"
import store from "./store"
import App from "./App.vue"
fetch('/user')
  .then(r => r.json())
  .then((user) => {
    store.commit('setUser', user)
    new Vue({
      store,
      render: (h) => h(App),
    }).$mount("#app")
  })
  .catch((error) => {
    // Don't forget to handle this
  })

Преимущество этого подхода заключается в том, что в ваше глобальное хранилище предварительно загружаются любые данные, которые могут потребоваться от API до загрузки приложения. Это удобный способ избежать ранее упомянутых проблем, связанных с резкими прыжками или управлением некоторой логикой загрузки.

Однако…

Здесь есть серьезная оговорка. Это правда, что вам не нужно беспокоиться о показе счетчика загрузки во время обработки HTTP-запроса, но пока что в вашем приложении ничего не отображается. Если ваше приложение является одностраничным, ваш пользователь может застрять, глядя на пустую белую страницу, пока запрос не вернется.

Таким образом, вы на самом деле не решаете проблему задержки, а просто решаете, какой интерфейс пользовательского интерфейса отображать, пока вы ждете данных.

Заключительное слово

У меня нет жестких правил о том, какой метод здесь лучше. На самом деле вы можете использовать все три в зависимости от того, какие данные вы получаете, и от того, что нужно вашему приложению.

Я также должен упомянуть, что хотя в моих примерах я делаю fetch запросов, а затем использую мутации Vuex для непосредственной фиксации в магазине. Вы можете так же легко использовать действия Vuex для реализации fetch. Вы также можете применить эти же принципы к любому другому инструменту управления состоянием, например Vue.observable.

На этом пока все, и если у вас есть какие-либо комментарии или вопросы, дайте мне знать. Twitter - отличное место, где можно связаться со мной, и вы можете подписаться на рассылку новостей, чтобы получать больше подобных обновлений.

Первоначально опубликовано на https://austingil.com