Как использовать vue-resource ($ http) и vue-router ($ route) в магазине vuex?

Раньше я получал детали фильма из сценария компонента. Функция сначала проверяет, совпадает ли идентификатор фильма в магазине с идентификатором фильма параметра маршрута. Если это то же самое, не получайте фильм из серверного API или получайте фильм из серверного API.

Он работал нормально. Но теперь я пытаюсь получить подробности фильма из мутации магазина. Однако я получаю сообщение об ошибке

Uncaught TypeError: невозможно прочитать свойство $ route неопределенного значения.

Как использовать vue-router ($route) для доступа к параметрам и vue-resource ($http) для получения из серверного API в магазине vuex?

store.js:

export default new Vuex.Store({
    state: {
        movieDetail: {},
    },
    mutations: {
        checkMovieStore(state) {
            const routerMovieId = this.$route.params.movieId;
            const storeMovieId = state.movieDetail.movie_id;
            if (routerMovieId != storeMovieId) {
                let url = "http://dev.site.com/api/movies/movie-list/" + routerMovieId + "/";
                this.$http.get(url)
                    .then((response) => {
                        state.movieDetail = response.data;
                    })
                    .catch((response) => {
                        console.log(response)
                    });
            }
        },
    },
});

компонентный скрипт:

export default {
    computed: {
        movie() {
            return this.$store.state.movieDetail;
        }
    },
    created: function () {
        this.$store.commit('checkMovieStore');
    },
}

person Benjamin Smith Max    schedule 02.03.2017    source источник


Ответы (5)


Чтобы использовать $http или $router в вашем магазине vuex, вам нужно будет использовать основной экземпляр vue. Хотя я не рекомендую использовать это, я добавлю то, что рекомендую, после ответа на фактический вопрос.


В вашем main.js или там, где вы создаете свой экземпляр vue, например:

new Vue({ 
  el: '#app',
  router,
  store,
  template: '<App><App/>',
  components: {
    App
  }
})

или что-то подобное, вы могли бы также добавить плагины vue-router и vue-resource.

Внесение небольшого изменения в это:

export default new Vue({ 
  el: '#app',
  router,
  store,
  template: '<App><App/>',
  components: {
    App
  }
})

Теперь я могу импортировать его в магазины vuex следующим образом:

//vuex store:
import YourVueInstance from 'path/to/main'

checkMovieStore(state) {
const routerMovieId = YourVueInstance.$route.params.movieId;
const storeMovieId = state.movieDetail.movie_id;
if (routerMovieId != storeMovieId) {
  let url = "http://dev.site.com/api/movies/movie-list/" + routerMovieId + "/";
  YourVueInstance.$http.get(url)
    .then((response) => {
       state.movieDetail = response.data;
     })
     .catch((response) => {
       console.log(response)
     });
  }
}

и, как сказано в ответе Austio, этот метод должен быть action, поскольку mutations не предназначены для обработки асинхронного режима.


Теперь перейдем к рекомендуемому способу этого.

  1. Ваш component может получить доступ к route params и передать его action.

    methods: {
      ...mapActions({
        doSomethingPls: ACTION_NAME
      }),
      getMyData () {
        this.doSomethingPls({id: this.$route.params})
      }
    }
    
  2. Затем action выполняет вызов через абстрактный служебный файл API (read _16 _ )

    [ACTION_NAME]: ({commit}, payload) {
       serviceWhichMakesApiCalls.someMethod(method='GET', payload)
         .then(data => {
            // Do something with data
         })
         .catch(err => {
            // handle the errors
         })
    }
    
  3. Ваш actions выполняет некоторую асинхронную работу и передает результат mutation.

    serviceWhichMakesApiCalls.someMethod(method='GET', payload)
         .then(data => {
            // Do something with data
            commit(SOME_MUTATION, data)
         })
         .catch(err => {
            // handle the errors
         })
    
  4. Mutations должны быть единственными, кто может изменить ваш state.

    [SOME_MUTATION]: (state, payload) {
       state[yourProperty] = payload
    }
    

Пример. Файл, содержащий список конечных точек. Он может вам понадобиться, если у вас есть разные этапы развертывания с разными конечными точками api, например: тестовая, промежуточная, производственная и т. д.

export const ENDPOINTS = {
  TEST: {
    URL: 'https://jsonplaceholder.typicode.com/posts/1',
    METHOD: 'get'
  }
}

И главный файл, реализующий Vue.http как службу:

import Vue from 'vue'
import { ENDPOINTS } from './endpoints/'
import { queryAdder } from './endpoints/helper'
/**
*   - ENDPOINTS is an object containing api endpoints for different stages.
*   - Use the ENDPOINTS.<NAME>.URL    : to get the url for making the requests.
*   - Use the ENDPOINTS.<NAME>.METHOD : to get the method for making the requests.
*   - A promise is returned BUT all the required processing must happen here,
*     the calling component must directly be able to use the 'error' or 'response'.
*/

function transformRequest (ENDPOINT, query, data) {
  return (ENDPOINT.METHOD === 'get')
      ? Vue.http[ENDPOINT.METHOD](queryAdder(ENDPOINT.URL, query))
      : Vue.http[ENDPOINT.METHOD](queryAdder(ENDPOINT.URL, query), data)
}

function callEndpoint (ENDPOINT, data = null, query = null) {
  return new Promise((resolve, reject) => {
    transformRequest(ENDPOINT, query, data)
      .then(response => { return response.json() })
      .then(data => { resolve(data) })
      .catch(error => { reject(error) })
  })
}

export const APIService = {
  test () { return callEndpoint(ENDPOINTS.TEST) },
  login (data) { return callEndpoint(ENDPOINTS.LOGIN, data) }
}

QueryAdder, если это важно, я использовал его для добавления параметров к URL-адресу.

export function queryAdder (url, params) {
  if (params && typeof params === 'object' && !Array.isArray(params)) {
    let keys = Object.keys(params)
    if (keys.length > 0) {
      url += `${url}?`
      for (let [key, i] in keys) {
        if (keys.length - 1 !== i) {
          url += `${url}${key}=${params[key]}&`
        } else {
          url += `${url}${key}=${params[key]}`
        }
      }
    }
  }
  return url
}
person Amresh Venugopal    schedule 03.03.2017
comment
Я использовал переменную для ссылки на основной экземпляр vue в действии. Это действие вызывается во время ловушки created компонента. В то время ссылка на основной экземпляр vue недоступна, что приводит к исключению. - person Teddy; 25.06.2017
comment
Это происходит только в том случае, если этот компонент загружается первым на этом маршруте, когда пользователь нажимает кнопку перезагрузки браузера. Если приложение уже загружено, переход к этому компоненту работает нормально. Есть ли обходной путь в этом случае? - person Teddy; 25.06.2017
comment
Вы пробовали рекомендованный мной метод? Использование экземпляра - не лучший способ использовать $ http. - person Amresh Venugopal; 25.06.2017
comment
Я не понял, как это сделать. Некоторые официальные примеры используют службу store и api, но вместо http-запросов они использовали только mock-объекты. Есть ссылки? - person Teddy; 25.06.2017
comment
YourVueInstance. $ Http не работал, но Vue.http как-то работает - person Teddy; 25.06.2017
comment
Я точно не помню, Vue.http действительно работает, но в одном сценарии он не помог. Я добавлю здесь пример. - person Amresh Venugopal; 25.06.2017
comment
Вспомогательные функции, которые я написал тогда, тоже используют Vue.http Тем не менее я отправлю его. - person Amresh Venugopal; 25.06.2017

Итак, несколько вещей, $ store и $ route, являются свойствами экземпляра Vue, поэтому доступ к ним внутри экземпляра Vuex не работает. Кроме того, мутации синхронны, вам нужны действия.

  1. Мутации => Функция, которая задает состояние и некоторые аргументы, изменяет состояние

  2. Действие => Выполняйте асинхронные действия, такие как HTTP-вызовы, а затем фиксируйте результаты в мутации.

Итак, создайте действие, которое отправляет http. Имейте в виду, что это псевдокод.

//action in store
checkMovieStore(store, id) {
  return $http(id)
    .then(response => store.commit({ type: 'movieUpdate', payload: response })
}

//mutation in store
movieUpdate(state, payload) {
  //actually set the state here 
  Vue.set(state.payload, payload)
}

// created function in component
created: function () {
   return this.$store.dispatch('checkMovieStore', this.$route.params.id);
},

Теперь созданная функция отправляет действие checkMovieStore с идентификатором, которое выполняет HTTP-вызов, после его завершения обновляет хранилище значением.

person Austio    schedule 02.03.2017

В вашем магазине vuex:

import Vue from 'vue'

Vue.http.post('url',{})

В отличие от обычных компонентов vue: this.$http.post(...)

person wissem maalel    schedule 07.07.2020

Я настоятельно рекомендую импортировать axios в модуль vuex (хранилище и подмодули) и использовать его для HTTP-запросов.

person David Soler    schedule 06.08.2019
comment
Почему? Я не понимаю, почему axios всегда лучший ответ. Можете ли вы объяснить, почему axios лучше в данном конкретном случае? - person nuts; 11.03.2020
comment
Основная причина - сообщество, вам будет проще находить решения для подобных проблем. - person David Soler; 03.04.2020
comment
Может быть, я хочу сказать следующее: возможно, объясните, почему вы рекомендуете другую библиотеку той, о которой на самом деле спрашивает человек. Что бы вы почувствовали, если бы вы спросили, что моя машина сломалась, может кто-нибудь помочь? и кто-то подходит и говорит, что тебе надо было просто купить другую машину. - person nuts; 03.04.2020
comment
И, возможно, ссылка на что-то вроде этого: medium.com/the-vue- point / retiring-vue-resource-871a82880af4 - person nuts; 03.04.2020

Для доступа к экземпляру vue в магазине используйте this._vm.
Но, как посоветовал Амреш, не используйте такие вещи, как $router в vuex

person NaN    schedule 06.08.2019