Мой компонент Vue выглядит так (упрощенно):
<template>
<div @click = "loadEvents">{{ loading }}</div>
</template>
<script setup>
import { ref } from 'vue'
let loading = ref(false)
loadEvents()
function loadEvents() {
const res = backendApi.getEvents(selectedDate.value)
loading = ref(res.loading)
}
</script>
Метод «backendApi.getEvents» выглядит так:
getEvents() {
const loading = ref(true)
axios.get(...).then(r => loading.value = false)
return { loading }
}
Проблема в том, что при начальной загрузке страницы загрузка правильно отображается на веб-сайте, сначала "true", а затем "false" после завершения запроса. Но когда функция «loadEvents» снова запускается при нажатии на div, значение «загрузка» просто остается «ложным» и не обновляется в DOM. Что я делаю неправильно и как я могу это исправить?
Я пытался сделать loading.value = res.loading.value, но это только меняет «загрузка» на «истина», а после выполнения запроса не меняется на «ложь».



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Краткий ответ будет таким:
Вы не можете переназначить реактивный объект, поскольку старые ссылки будут указывать на предыдущее значение объекта, и реактивность будет потеряна. Все реактивные переменные должны быть объявлены с помощью
const.
Но, кажется, вы слишком усложнили логику. Насколько я понимаю, loading должно быть истинным, когда axios загружает данные (просто сделайте правильный импорт):
<template>
<div @click = "loadEvents">{{ loading }}</div>
</template>
<script setup>
const loading = ref(false);
loadEvents()
async function loadEvents() {
loading.value = true;
const results = await backendApi.getEvents(selectedDate.value);
// use results here
// ...
loading.value = false;
}
function getEvents() {
return axios.get(...);
}
</script>Но вы можете улучшить это и повторно использовать loading, переместив его в бэкэнд API:
// your backend-api.js:
export const loading = ref(false);
export const backendApi = {
getEvents(){
return this.callApi(...);
},
async callApi(...args){
loading.value = true;
const result = await axios.get(...args);
loading.value = false;
return result;
}
};
// your component
<template>
<div @click = "loadEvents">{{ loading }}</div>
</template>
<script setup>
import {loading, backendApi} from './backend-api.js';
loadEvents()
async function loadEvents() {
const results = await backendApi.getEvents(selectedDate.value);
// use results here
// ...
}
</script>ОБНОВЛЕНИЕ
После некоторого обсуждения с автором появилось новое решение.
Автор указал, что для каждого бэкэнд-вызова требуется ссылка загрузки. Перенести загрузку в бэкэнд API не так уж и хорошо, нам нужна некоторая обертка вызовов API и так далее. Вся логика становится НЕ агностической, что выглядит не идеально. Итак...
Недавно я стал поклонником сеттера с некоторыми действиями, и сеттеры обещаний — мой новый фаворит. Обычно у меня есть какой-то модуль с моими утилитами Vue, поэтому в этом случае я создал promiseRef(), который возвращает логическую ссылку с дополнительным свойством «обещание», которое контролирует его значение false/true. Таким образом, у нас есть загрузка ссылок, независимая от какой-либо другой логики, включая некоторый внутренний API как ненужную зависимость. Итак, вся установка выглядит так:
<script setup>
import { backendApi } from './backend-api.js';
import { promiseRef } from './utils.js'
const loading = promiseRef();
async function loadEvents(){
const result = await (loading.promise = backendApi.getEvents());
// use your results
}
</script>
<template>
<button @click = "loadEvents">{{ loading }}</button>
</template>
бэкэнд-api.js:
// just emulating some api call
export const backendApi = {
getEvents(){
return new Promise(resolve => setTimeout(resolve, 2000));
}
}
utils.js:
import { ref } from 'vue';
export const promiseRef = value => {
const loading = ref(value || false);
let promise;
Object.defineProperty(loading, 'promise', {
get(){
return promise;
},
set(val){
loading.value = true;
(promise = val).then(() => loading.value = false);
}
});
return loading;
};
Работающая суть Vue3 уже здесь!
В вашем втором решении, как я могу иметь переменную «загрузки», специфичную для каждой функции? BackendApi имеет несколько функций, которые можно вызывать одновременно. В этом случае я не смог бы отличить, какой из них все еще загружается, а какой нет с вашим решением.
@Lukas тогда использует первое решение :)
Что ж, да, это решение работает, но тогда у меня дублируется код везде, где я вызываю метод из backendApi, потому что мне нужно установить загрузку до запроса, а затем после запроса.
позже приду к решению)
Хорошо, буду ждать, спасибо :)
@Lukas, ты понял, загляни в раздел ОБНОВЛЕНИЕ :)
@Lukas обновил начало ответа, объяснив, почему переназначение не сработает.
FWIW, вы, похоже, воспроизводите функциональность Suspense, обрабатывая статус загрузки вручную.