AJAX обертка для Vuex - PullRequest
       27

AJAX обертка для Vuex

1 голос
/ 25 января 2020

Текущая ситуация

Я реализовал плагин Vue, который управляет вызовами к серверу с помощью очередей.

У меня есть пользовательский интерфейс автосохранения, который всякий раз, когда пользователь оставляет вход, Код на стороне клиента отправляет UiState на сервер для хранения в БД. Так как пользователь может вводить и оставлять много входов за короткое время, Ajax Запросы могут не поступать с сервера последовательно.

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

Вот почему я реализовал следующий плагин для Vue. Он использует Очередь и, если к тому времени, когда сервер отвечает на запрос на сохранение, был запущен новый запрос на сохранение, он отправляет только последний запрос на сохранение.

import axios from "Axios"
import qs from 'qs';

export default {
    install(Vue, pluginOptions) {
        var ajs = {}; //ajax-saving

        var defaultPluginOptions = {
            indicatorElement: null
        };

        // saving flags
        ajs.isSaving = false;
        ajs.pendingSaving = false;
        ajs.pendingRequests = [];


        // the indicator element
        ajs.indicator = null;
        ajs.indicatorTitle = null;

        Vue.prototype.$ajaxSaveData = function (serverUrl, data, callback) {
            //use pending flag for the function to recall itself when it finishes
            if (ajs.isSaving) {
                ajs.pendingSaving = true;
                ajs.pendingRequests.push({ serverUrl: serverUrl, data: data, callback: callback });
                console.log('%cAjaxSaving %cThe save action is queued!', 'color:#67A9BF;', 'color:#E3DC29');
                return;
            }

            onSaveStarted();

            axios({
                    url: serverUrl,
                    method: 'POST',
                    headers: { 'content-type': 'application/x-www-form-urlencoded' },
                    data: qs.stringify(data),
                })
                .then(result => {
                    onSaveEnded();
                    if (callback) callback(true, result);
                })
                .catch(function (error) {
                    console.log(error);

                    onSaveEnded(true);
                    if (callback) callback(false);
                })

        }

        // saving events
        var onSaveStarted = function () {
            ajs.isSaving = true;

            ajs.indicator.removeClass('error');
            ajs.indicator.addClass('saving');
            ajs.indicator.show();
            ajs.indicatorTitle.html("Saving");

            console.log('%cAjaxSaving %cStarted!', 'color:#67A9BF;', 'color:#7DBF67');
        }
        var onSaveEnded = function (error) {
            ajs.isSaving = false;
            ajs.indicator.removeClass('saving');

            if (error) {
                ajs.indicator.addClass('error');
                ajs.indicatorTitle.html("Error");
                console.log('%cAjaxSaving %cError!', 'color:#67A9BF;', 'color:#E60909');
            } else {
                ajs.indicatorTitle.html("Saved");
            }

            console.log('%cAjaxSaving %cEnded!', 'color:#67A9BF;', 'color:#091152');

            // execute the pending savings
            if (ajs.pendingSaving) {

                var pendingToProcess = ajs.pendingRequests;
                ajs.pendingRequests = [];
                ajs.pendingSaving = false;

                console.log('%cAjaxSaving %cExecuting Pending!', 'color:#67A9BF;', 'color:#E83CC5');

                // process all pending request found in stack.
                // skip older requests if newer are found
                var req;
                var processedRequests = [];
                while ((req = pendingToProcess.pop()) != null) {
                    if (!processedRequests.includes(req.serverUrl)) {
                        processedRequests.push(req.serverUrl);
                        var executingReq = req;
                        setTimeout(function () {
                            Vue.prototype.$ajaxSaveData(executingReq.serverUrl, executingReq.data, executingReq.callback);
                        }, 0);
                    }
                }

            }
        }

        var testOptions = function () {

            if (!ajs.options.indicatorElement) {
                throw ('indicator element is not set');
            }
        }

        //TODO: Remove jquery
        // Plugin initialization
        var createIndicator = function () {
            ajs.indicator = $(ajs.options.indicatorElement);
            ajs.indicatorTitle = ajs.indicator.find('span');
            if (ajs.indicatorTitle.length === 0) {
                ajs.indicatorTitle = $('<span></span>');
                ajs.indicator.append(ajs.indicatorTitle);
            }
        }
        var initialize = function () {
            //initialize options and elements needed from plugin
            ajs.options = $.extend({}, defaultPluginOptions, pluginOptions);
            testOptions();
            createIndicator();
        };


        initialize();
    }
}

Чтобы использовать это:

import Vue from "vue";
import VueAjaxSaving from '@/Plugins/VueAjaxSaving'
Vue.use(VueAjaxSaving, { indicatorElement: '#ajaxIndicator' });

new Vue({
  data: {
        uiState: {....}
  },
  methods:{
    updateUiState() {
       this.$ajaxSaveData('/Page/UpdateUiState', { uiState: this.uiState }, () => this.someCallback());
    },
  }
})

Плагин принимает в качестве параметра элемент (селектор jQuery) и обновляет пользовательский интерфейс с состоянием сохранения.

Необходимость

Я рефакторинг своего кода для использования Vuex для моего состояния, и я хочу, чтобы модуль Vuex обрабатывал сохранение, когда что-то мутировало.

Я хочу предоставить своим модулям Vuex функцию, которая обрабатывает сохранение так же, как мои компоненты Vue. С легкостью вызова ajaxSaveData('url',data).then(...) из любого действия Vuex.

Мне нужно обновить часть пользовательского интерфейса со статусом сохранения.

Вопросы

Что мне нужно использовать для удовлетворения моих потребностей?

  • Плагин Vuex?
  • Функция класса, которая импортируется в файл модуля и используется как грязная, как звучит?
  • Новый модуль, который содержит все логики сохранения c ajaxSaveData? Я полагаю, что любой модуль может отправлять свои действия.

Как обновить пользовательский интерфейс для ajaxSaveData Vuex (плагин / класс / модуль)?

1 Ответ

0 голосов
/ 26 января 2020

Хорошая функция / плагин может быть добавлена ​​к Vuex под названием подписка . Вы создаете плагин, просто добавляя к нему функцию подписки, например:

const plugins = [
  store => {
    store.subscribe((mutation, state) => {
      // your code that should happen on a mutation - it receives the full state object
    })
  }
]

export const store = new Vuex.Store({
  state,
  mutations,
  actions,
  getters,
  plugins // you have to add plugins to the Vuex store
})

Подробнее о Плагины Vuex : https://vuex.vuejs.org/guide/plugins.html

Подробнее о Vuex подписаться : https://vuex.vuejs.org/api/#subscribe

Что касается серверной части : я бы добавил метку времени к моему запросу и бэкэнд сравнивает его с текущей доступной версией - если полученные данные «старше» данных в БД, просто отправьте обратно действительные данные.

...