У меня есть страница макета, и каждый компонент наследуется от нее. Моя проблема в том, что функция onMount вызывается дважды для каждого компонента. Это создает много проблем, потому что моя функция onMount содержит вызовы API. Это означает, что вызовы к API дублируются. Долгое время я не знал, почему так происходит. Недавно я заметил, что компонент макета имеет обертку, которая запускает переход. Когда я удалил переход, onMount был вызван один раз, как и ожидалось. С другой стороны, я не хочу потерять переход, потому что страница выглядит плохо. Как решить эту проблему?
PageTransitions.svelte:
<script>
import { fly } from "svelte/transition";
export let refresh = "";
</script>
{#key refresh}
<div in:fly = {{ y: -50, duration: 250, delay: 300 }} out:fly = {{ y: -50, duration: 250 }} class = "flex flex-1">
<slot>Zawartość</slot>
</div>
{/key}
layout.svelte:
<script>
import { googleMap } from "./../stores.js";
import { page } from "$app/stores";
import Footer from "$lib/Footer.svelte";
import Header from "$lib/Header.svelte";
import PageTransitions from "$lib/PageTransitions.svelte";
import Notifications from "svelte-notifications";
import "../app.css";
import { GOOGLE_API_KEY } from "$lib/constants.js";
import { setContext } from "svelte";
import { writable } from "svelte/store";
const url = `https://maps.googleapis.com/maps/api/js?key=${GOOGLE_API_KEY}&libraries=places,directions`;
let isSideMenuOpen = setContext("isSideMenuOpen", writable(false));
$: loaded = $googleMap.loaded;
const onLoad = () => {
$googleMap.loaded = true;
console.info("Google Maps SDK loaded…", window.google);
};
</script>
<svelte:head>
{#if !loaded}
<script
src = {url}
type = "application/javascript"
defer
async
on:load = {onLoad}></script>
{/if}
</svelte:head>
<Notifications>
<div
class = "h-screen flex flex-col bg-gray-50 dark:bg-gray-900"
class:overflow-hidden = {isSideMenuOpen}
>
<Header />
<PageTransitions refresh = {$page.path}>
<slot>Strona</slot>
</PageTransitions>
<Footer />
</div>
</Notifications>
modules.svelte (пример компонента):
<script context = "module">
export const ssr = false;
export const prerender = true;
export async function load({ session }) {
if (!session.authenticated) {
return {
status: 302,
redirect: "/auth/login",
};
}
return {
props: {
authenticated: session.authenticated,
token: session.token,
user: session.user,
},
};
}
</script>
<script>
import { slide } from 'svelte/transition';
import GenericTable from "$lib/GenericTable.svelte";
import { profile, modules } from "../../stores";
import { getModuleDataURL, getModuleTypeDataURL, get, del } from "$lib/api";
import GenericRow from "$lib/Rows/ModuleRow.svelte";
import { onMount } from "svelte";
import Loader from "$lib/Loader.svelte";
import { getNotificationsContext } from "svelte-notifications";
const { addNotification } = getNotificationsContext();
let loading = false;
export let authenticated;
export let token;
export let user;
const siteHeader = "Moduły";
const rootPath = "modules";
let filters = {
moduleType: null,
imei: "",
serialNumber: "",
phoneNumber: "",
isActive: null,
desc: "",
id: null,
};
const filtersChange = () => {
let filteredItems = [...$modules.items];
$modules.filters = false;
if (
filters.moduleType ||
filters.id ||
filters.desc.length > 2 ||
filters.isActive !== null ||
filters.imei.length > 2 ||
filters.serialNumber.length > 2 ||
filters.phoneNumber.length > 2
) {
$modules.filters = true;
}
if (filters.moduleType) {
filteredItems = filteredItems.filter(
(item) => item.moduleType.id === filters.moduleType.value
);
}
if (filters.isActive !== null) {
filteredItems = filteredItems.filter(
(item) => item.isActive === filters.isActive
);
}
if (filters.imei.length > 2) {
filteredItems = filteredItems.filter((item) =>
item.imei.includes(filters.imei)
);
}
if (filters.serialNumber.length > 2) {
filteredItems = filteredItems.filter((item) =>
item.serialNumber.includes(filters.serialNumber)
);
}
if (filters.phoneNumber.length > 2) {
filteredItems = filteredItems.filter((item) =>
item.phoneNumber.includes(filters.phoneNumber)
);
}
if (filters.desc.length > 2) {
filteredItems = filteredItems.filter((item) =>
item.desc ? item.desc.includes(filters.desc) : false
);
}
if (filters.id === "DESC") {
filteredItems.sort((a, b) => (a.id > b.id ? 1 : -1));
} else if (filters.id === "ASC") {
filteredItems.sort((a, b) => (a.id < b.id ? 1 : -1));
}
$modules.filteredModules = filteredItems;
};
const urlModuleType = getModuleTypeDataURL();
const urlModuleData = getModuleDataURL();
const deleteItem = async (id) => {
loading = true;
const url = getModuleDataURL() + "?id = " + id;
try {
const result = await del(url, token, false);
if (result.status) {
$modules.items = $modules.items.filter((item) => id !== item.id);
$modules.filteredModules = $modules.filteredModules.filter(
(item) => id !== item.id
);
addNotification({
text: "Usunięto moduł o id - " + id,
position: "bottom-right",
type: "success",
removeAfter: 4000,
});
} else throw result.message;
} catch (err) {
console.error(err);
addNotification({
text: err,
position: "bottom-right",
type: "danger",
removeAfter: 4000,
});
}
loading = false;
};
onMount(async () => {
loading = true;
$modules.filters = false;
$modules.filteredGroups = [];
try {
const resultModules = await get(
urlModuleData + "?$orderby=isActive desc,id",
token
);
if (resultModules.data) $modules.items = resultModules.data.items;
const resultModuleTypes = await get(urlModuleType, token);
if (resultModuleTypes.data)
$modules.moduleTypes = resultModuleTypes.data.items;
} catch (err) {
console.error(err);
addNotification({
text: "Podczas komunikacji z serwerem wystąpił błąd. Spróbuj raz jeszcze. Jeśli problem się powtórzy, skontaktuj się z administratorem aplikacji.",
position: "bottom-right",
type: "danger",
removeAfter: 4000,
});
}
loading = false;
});
let virtualListData = [];
$: virtualListData = $modules.filters
? $modules.filteredModules
: $modules.items;
let moduleTypes = [];
$: moduleTypes = $modules.moduleTypes.map((item) => ({
value: item.id,
label: item.type,
}));
const widths = ["5%", "15%", "10%", "10%", "15%", "15%", "20%", "10%"];
let listTableHead = [];
$: listTableHead = [
{
id: 1,
name: "ID",
filter: true,
type: "id",
},
{
id: 2,
name: "IMEI",
filter: true,
label: "imei",
type: "text",
placeholder: "Wpisz minimum 3 znaki",
onInput: filtersChange,
},
{
id: 3,
name: "Typ modułu",
filter: true,
label: "moduleTypes",
type: "select",
placeholder: "-",
items: moduleTypes,
select: (e) => {
filters.moduleType = e.detail;
filtersChange();
},
clear: () => {
filters.moduleType = null;
filtersChange();
},
},
{
id: 4,
name: "Aktywność",
filter: true,
label: "isActive",
type: "select",
placeholder: "-",
items: [
{ value: true, label: "Aktywny" },
{ value: false, label: "Nieaktywny" },
],
select: (e) => {
filters.isActive = e.detail.value;
filtersChange();
},
clear: () => {
filters.isActive = null;
filtersChange();
},
},
{
id: 5,
name: "Numer telefonu",
filter: true,
label: "phoneNumber",
type: "text",
placeholder: "Wpisz minimum 3 znaki",
onInput: filtersChange,
},
{
id: 6,
name: "Numer seryjny",
filter: true,
label: "serialNumber",
type: "text",
placeholder: "Wpisz minimum 3 znaki",
onInput: filtersChange,
},
{
id: 7,
name: "Opis",
filter: true,
label: "desc",
type: "text",
placeholder: "Wpisz minimum 3 znaki",
onInput: filtersChange,
},
{
id: 8,
name: "Dodaj nowy moduł",
type: "addNew",
},
];
</script>
<main class = "w-full flex flex-1 overflow-y-auto bg-red-100">
<div class = "p-2 overflow-y-auto flex flex-1">
{#if loading}
<Loader />
{/if}
<div
transition:slide
style = "display:{loading ? 'none' : 'block'}"
class = "flex flex-col flex-1 items-center justify-center"
>
<GenericTable
{siteHeader}
{filtersChange}
{deleteItem}
{virtualListData}
{widths}
{listTableHead}
{filters}
{rootPath}
{GenericRow}
/>
</div>
</div>
</main>
Что я пробовал:
Почему это происходит?
Да, когда я перемещаюсь между страницами, значение меняется. Я получил: Refresh => / Refresh => / Groups Refresh => / Modules Refresh => / Objects Refresh => / Users Refresh => / Routes Итак, Refresh равен текущему пути к странице. Любая идея, как правильно реализовать корневой переход?
Существует связанная проблема, задокументированная здесь (без решения).
Основной причиной является проблема времени.
layout.svelte
заменяет <slot/>
на PageTransitions.svelte
.
Это приводит к загрузке нового слота/страницы и даже запуску onMount.
Новая страница также будет видна на экране в течение нескольких миллисекунд, поскольку перехода еще не произошло!
После изменения слота Svelte запускает магазин $page
для обновления.
В вашем коде это приводит к переходу на обновление ({#key refresh}
) и, следовательно, к повторной загрузке <slot/>
.
Невозможно изменить время между обменом <slot/>
и магазином $page
без изменения самого Svelte.
Этот код запускает переход и вызывает монтирование только одного компонента.
layout.svelte:
<script context = "module">
export const load = async ({ url }) => ({ props: { refresh: url } });
</script>
<script>
import { googleMap } from "./../stores.js";
import Footer from "$lib/Footer.svelte";
import Header from "$lib/Header.svelte";
import PageTransitions from "$lib/PageTransitions.svelte";
import Notifications from "svelte-notifications";
import "../app.css";
import { GOOGLE_API_KEY } from "$lib/constants.js";
import { setContext } from "svelte";
import { writable } from "svelte/store";
export let refresh;
const url = `https://maps.googleapis.com/maps/api/js?key=${GOOGLE_API_KEY}&libraries=places,directions`;
let isSideMenuOpen = setContext("isSideMenuOpen", writable(false));
$: loaded = $googleMap.loaded;
const onLoad = () => {
$googleMap.loaded = true;
console.info("Google Maps SDK loaded…", window.google);
};
</script>
<svelte:head>
{#if !loaded}
<script
src = {url}
type = "application/javascript"
defer
async
on:load = {onLoad}></script>
{/if}
</svelte:head>
<Notifications>
<div
class = "h-screen flex flex-col bg-gray-50 dark:bg-gray-900"
class:overflow-hidden = {isSideMenuOpen}
>
<Header />
<PageTransitions {refresh}>
<slot>Strona</slot>
</PageTransitions>
<Footer />
</div>
</Notifications>
Для устранения неполадок, не могли бы вы добавить
$: console.info(refresh);
в свой PageTransitions.svelte? Я подозреваю, что переменная «обновить» изменяется, и поэтому ваша страница перезагружается из-за {#обновления ключа}