@alenov
Программист

Как в Vue-приложении сначала загрузить данные в $store, а уже потом рендерить шаблоны?

У меня приложение на однофайловых компонентах. Vue, Vuex, axios ...
Использую Vue CLI, Webpack.
В моём приложении есть Vuex хранилище $store. Данные из $store используются в шаблонах компонентов. $store заполняется данными в основном приложении App. Краткая структура:

main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
...

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')


App.vue
<template>
  <div id="app">
    <v-app>
      <MainContent/>
    </v-app>
  </div>
</template>

<script>
import MainContent from '@/components/MainContent'
import axios from 'axios'
import store from '@/store'

export default {
  name: 'App',
  components: {
    MainContent,
  },
  data: () => {
    return {
      ...
    }
  },
  mounted () {
    const vm = this
    async function loader () {
      const response = await axios.post('https://site.ru/get_data')
      if (response.status === 200) {
        await store.dispatch('setData', response.data.Data)
      }
    }
    loader()
  }
}
</script>

Маршрут / загружает в MainContent компонент, который требует наличия данных, которые загружаются в mounted.
Проблема в том, что рендеринг этого компонента начинается тогда, когда данные ещё не загружены. async и await никакого эффекта не дают!
Т.е. если пользователь нажмёт F5 на любой странице сайта, которая содержит зависимый от $store компонент, то получит ошибку.
Как загрузить в хранилище данные до рендеринга шаблонов? Можно конечно в шаблонах поставить v-if с условием наличия данных. Но в файле компонента есть не только шаблон, но и код, который может содержать хуки жизненного цикла, и им могут понадобиться данные из $store. Собственно, так и есть. Можно и там поставить промисы, но это как-то очень не по феншую.
Есть красивое решение?
  • Вопрос задан
  • 1348 просмотров
Решения вопроса 2
IgorPI
@IgorPI
const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // вызывается до подтверждения пути, соответствующего этому компоненту.
    // НЕ ИМЕЕТ доступа к контексту экземпляра компонента `this`,
    // так как к моменту вызова экземпляр ещё не создан!
  },
  beforeRouteUpdate (to, from, next) {
    // вызывается когда маршрут, что рендерит этот компонент изменился,
    // но этот компонент будет повторно использован в новом маршруте.
    // Например, для маршрута с динамическими параметрами `/foo/:id`, когда мы
    // перемещаемся между `/foo/1` и `/foo/2`, экземпляр того же компонента `Foo`
    // будет использован повторно, и этот хук будет вызван когда это случится.
    // Также имеется доступ в `this` к экземпляру компонента.
  },
  beforeRouteLeave (to, from, next) {
    // вызывается перед переходом от пути, соответствующего текущему компоненту;
    // имеет доступ к контексту экземпляра компонента `this`.
  }
}


beforeRouteEnter (to, from, next) {
  next(vm => {
    // экземпляр компонента доступен как `vm`
  })
}


Источник
Ответ написан
@alenov Автор вопроса
Программист
Покопался с роутером, как советовал Игорь , и решил задачу через глобальный хук beforeEach. Получилось кратко, красиво, и в одном месте.
router/index.js
import store from '@/store'
import axios from 'axios'
...
router.beforeEach((to, from, next) => {
  if (!store.state.Data) {
    axios.post('https://site.ru/get_data').then(response => {
        store.dispatch('setData', response.data.Data)
        next()
  } else {
    next()
  }
})


Теперь переход на страницу выполняется только в случае, если данные загружены, а если не загружены - предварительно загружаются. Можно забыть про проверку наличия данных в компонентах.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
Kozack
@Kozack Куратор тега Vue.js
Thinking about a11y
Алексей,

Ну дык за это и весь базар)))
Я хочу, чтобы независимо от того, какую страницу открыл юзер, данные для шаблона загрузились бы первые, а потом уже выполнялся рендеринг, для которого эти данные нужны. И пусть он видит афигенный индикатор загрузки в этот момент))
Юзер ведь может, например, сохранить у себя ссылку на какую-то внутреннюю страницу сайта, которой нужен storage. А потом её просто открыть. Как я и говорил, решение есть: условный рендеринг template через v-if, промисы в хуках и пр. и пр. И это в каждом таком компоненте. Я хочу это в одном месте сделать, и забыть о том, что нужна проверка в


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

Например компонент шапки может не дожидаться пока загрузится фид. И не дожидаясь пока загрузится информация о пользователе отобразить меню сайта. Или какая-то форма может полностью отобразится не дожидаясь информации об пользователе, но блокировать отправку пока эта информация не будет загружена.

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

Во-вторых, если вам вот настолько чешется сделать всё в одном месте -- условный рендеринг в App в котором выполняется запрос.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы