You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Svelte 5项目登录登出实现及数据实时更新问题求助

问题描述

我正在开发一个校园网站前端,用户无需注册,仅支持登录/登出操作。目前登录页面已开发完成,但存在两个问题:

  • 用户数据无法实时更新,首页必须刷新才能获取最新用户信息;
  • 不知道如何实现按钮触发的登出功能。

现有实现代码

login/+page.server.js

import { createSession, getUser } from '$lib/fetchData.svelte.js';
import { redirect } from '@sveltejs/kit';

export const actions = {
  default: async (event) => {
    const data = await event.request.formData();
    const user = getUser({
      firstName: data.get('firstName'),
      lastName: data.get('lastName'),
      year: data.get('year'),
      section: data.get('section')
    })

    event.cookies.set('sessionId', createSession(user), { path: '/'});

    redirect(302, '/');
    return { success: true }
  }
}

login/+page.svelte

<script>
  import { Button } from "$lib/components/ui/button";
  import * as Card from "$lib/components/ui/card";
  import { Input } from "$lib/components/ui/input";
  import { Label } from "$lib/components/ui/label";
  import * as Select from "$lib/components/ui/select";
  import { Checkbox } from "$lib/components/ui/checkbox";
  import { getContext, setContext } from "svelte";
  import { goto } from "$app/navigation";
  import { applyAction, enhance } from '$app/forms';

  let { data, form } = $props();

  let year = $state(1);
  let section = $state('A');
  let sections = $derived(generateAlphabetArray(getSections(year)));
  let teacher = $state(false);

  function generateAlphabetArray(length) {
    const alphabet = 'ABCDEFGHILMNOPQRSTUVWXYZ';
    return Array.from({ length }, (_, i) => alphabet[i % alphabet.length]);
  }

  function getSections(classe) {
    const classes = [
      {
        classe: 1,
        sezioni: 12
      },
      {
        classe: 2,
        sezioni: 11
      },
      {
        classe: 3,
        sezioni: 12
      },
      {
        classe: 4,
        sezioni: 11
      },
      {
        classe: 5,
        sezioni: 10
      }
    ]
    return classes.find(c => c.classe === classe).sezioni;
  }

  function changeClass(e) {
    year = e.value;
    if (!sections.includes(section))
    section = sections[0];
  }
</script>
<div class="h-full grid items-center">
<Card.Root class="mx-auto max-w-sm">
  <Card.Header>
    <Card.Title class="text-xl">Accedi</Card.Title>
  </Card.Header>
  <Card.Content>
    <form 
      class="grid gap-4" 
      method="POST" 
      use:enhance={({ formElement, form, action, cancel, submitter }) => {
        return async ({ result }) => {
          if (result.type === 'redirect') {
            goto(result.location);
          } else {
            await applyAction(result);
          }
        }
      }}
    >
      <div class="grid gap-2">
        <Label for="first-name">Nome</Label>
        <Input 
          id="first-name" 
          placeholder="Mario" 
          name="firstName"
          required
        />
      </div>
      <div class="grid gap-2">
        <Label for="last-name">Cognome</Label>
        <Input 
          id="last-name" 
          placeholder="Rossi" 
          name="lastName"
          required
        />
      </div>
      <div class="flex gap-2">
        <Label for="teacher">Professore</Label>
        <Checkbox bind:checked={teacher} id="teacher" name="teacher"/>
      </div>
      {#if !teacher}
        <div class="grid grid-cols-2 gap-4">
          <div class="grid gap-2">
            <Label for="year">Classe</Label>
            <Select.Root
              name="year"
              id="year"
              selected={{label: '1', value: year}}
              onSelectedChange={changeClass}
            >
              <Select.Trigger class="w-full">
                <Select.Value placeholder="1"/>
              </Select.Trigger>
              <Select.Content>
                {#each {length: 5} as _, i}
                  <Select.Item value={i + 1}>{i + 1}</Select.Item>
                {/each}
              </Select.Content>
            </Select.Root>
          </div>
          <div class="grid gap-2">
            <Label for="section">Sezione</Label>
            <Select.Root
              name="section"
              id="section"
              selected={{label: section, value: section}}
              onSelectedChange={(e) => section = e.value}
            >
              <Select.Trigger class="w-full">
                <Select.Value placeholder="A"/>
              </Select.Trigger>
              <Select.Content>
                {#each sections as letter}
                  <Select.Item value={letter} class="text-center">{letter}</Select.Item>
                {/each}
              </Select.Content>
            </Select.Root>
          </div>
        </div>
      {/if}
      <Button 
        type="submit" 
        class="w-full" 
      >
        Accedi
      </Button>
    </form>
    <div class="mt-4 text-center text-sm">
      Se non riesci ad accedere manda una mail all'indirizzo
      <p class="underline"> dynamo.studenti@gmail.com </p>
    </div>
  </Card.Content>
</Card.Root>
</div>

/+layout.server.js

export const load = async (event) => {
  return {
    user: event.locals.user
  }
}

/+hooks.server.js

import { getUserFromSession } from '$lib/fetchData.svelte';

export const handle = async ({ event, resolve }) => {
  event.locals.user = await getUserFromSession(event.cookies.get('sessionId'));

  return resolve(event);
}

/+layout.svelte

<script>
    import { setContext } from 'svelte';
    import '../app.css';
    let { children, data } = $props();
    setContext('user', data.user); 
</script>
<main class="container flex flex-col gap-2 py-4 h-screen p-2 sm:p-4 overflow-hidden">
{@render children()}
</main>

问题解决方案

1. 修复登录后用户数据不实时更新问题

当前登录后手动调用goto跳转,不会自动刷新布局数据,导致data.user仍为旧值。可通过两种方式解决:

方式一:使用默认enhance行为

SvelteKit的enhance默认会在表单提交后自动更新页面数据,移除自定义逻辑即可:

<form 
  class="grid gap-4" 
  method="POST" 
  use:enhance
>

方式二:跳转前刷新所有数据

如果需要保留自定义逻辑,在goto前调用invalidateAll强制刷新数据:

<script>
  // 新增导入
  import { invalidateAll } from '$app/navigation';
  // ...其他代码
</script>

<form 
  class="grid gap-4" 
  method="POST" 
  use:enhance={({ formElement, form, action, cancel, submitter }) => {
    return async ({ result }) => {
      if (result.type === 'redirect') {
        await invalidateAll();
        goto(result.location);
      } else {
        await applyAction(result);
      }
    }
  }}
>

2. 实现登出功能

步骤1:添加服务器端登出逻辑

在根目录创建+page.server.js,添加登出动作:

import { redirect } from '@sveltejs/kit';

export const actions = {
  logout: async (event) => {
    // 清除sessionId cookie
    event.cookies.set('sessionId', '', { 
      path: '/',
      expires: new Date(0)
    });
    redirect(302, '/');
  }
}

步骤2:添加登出按钮

在需要显示登出按钮的组件(如导航栏)中添加表单:

<script>
  import { Button } from "$lib/components/ui/button";
  import { enhance } from '$app/forms';
</script>

<form method="POST" action="?/logout" use:enhance>
  <Button type="submit">登出</Button>
</form>

enhance会自动处理表单提交并刷新页面数据,确保登出后用户信息实时清除。


内容的提问来源于stack exchange,提问作者mattia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 22:05:53