如果您习惯于构建客户端应用程序,那么在跨越服务器和客户端的应用程序中进行状态管理可能会显得令人生畏。本节提供了一些避免常见问题的技巧。 ## 避免在服务器上共享状态 浏览器是*有状态的*——状态在用户与应用程序交互时存储在内存中。另一方面,服务器是*无状态的*——响应内容完全由请求内容决定。 从概念上讲,是这样的。实际上,服务器通常寿命长且被多个用户共享。因此,不要在共享变量中存储数据是很重要的。例如,考虑以下代码: ```js // @errors: 7034 7005 /// file: +page.server.js let user; /** @type {import('./$types').PageServerLoad} */ export function load() { return { user }; } /** @satisfies {import('./$types').Actions} */ export const actions = { default: async ({ request }) => { const data = await request.formData(); // NEVER DO THIS! user = { name: data.get('name'), embarrassingSecret: data.get('secret') }; } } ``` 用户变量`user`被所有连接到此服务器的用户共享。如果 Alice 提交了一个令人尴尬的秘密,而 Bob 在她之后访问了页面,Bob 就会知道 Alice 的秘密。此外,当 Alice 在当天稍后返回网站时,服务器可能已经重新启动,导致她丢失数据。 相反,您应该使用*身份验证*来验证用户,并使用[`cookies`](load#Cookies)将数据持久化到数据库中。 ## 无副作用 由于相同的原因,您的 `load` 函数应该是 *纯函数* — 没有副作用(除了偶尔的 `console.log(...)`)。例如,您可能会在 `load` 函数内部写入 store 或全局状态,以便您可以在组件中使用该值: ```js /// file: +page.js // @filename: ambient.d.ts declare module '$lib/user' { export const user: { set: (value: any) => void }; } // @filename: index.js // ---cut--- import { user } from '$lib/user'; /** @type {import('./$types').PageLoad} */ export async function load({ fetch }) { const response = await fetch('/api/user'); // NEVER DO THIS! user.set(await response.json()); } ``` 与前面的例子一样,这会将一个用户的信息放在所有用户共享的地方。相反,只需返回数据... ```js /// file: +page.js /** @type {import('./$types').PageServerLoad} */ export async function load({ fetch }) { const response = await fetch('/api/user'); +++ return { user: await response.json() };+++ } ``` ...并将其传递给需要它的组件,或使用[`page.data`](load#page.data)。 如果您不使用 SSR,那么就不会存在意外将一个用户的数据暴露给另一个用户的风险。但您仍然应该避免在您的`load`函数中出现副作用——没有它们,您的应用程序将更容易理解。 ## 使用状态和存储与上下文 您可能会想知道,如果我们不能使用全局状态,我们是如何使用`page.data`和其他[应用状态]($app-state)(或[应用存储]($app-stores))。答案是服务器上的应用状态和应用存储使用 Svelte 的[上下文 API](/tutorial/svelte/context-api)——状态(或存储)通过`setContext`附加到组件树,当您订阅时,您可以使用`getContext`检索它。我们也可以用我们自己的状态做同样的事情: ```svelte ``` ```svelte
Welcome {user().name}
``` > \[!注意\] 我们将一个函数传递给`setContext`以保持边界之间的响应性。了解更多信息[这里](/docs/svelte/$state#Passing-state-into-functions) > \[!旧版\] 您还使用来自 `svelte/store` 的存储库,但使用 Svelte 5 时,建议使用通用响应性。 更新在页面通过 SSR 渲染过程中更深层次的页面或组件的基于上下文的状态值时,不会影响父组件中的值,因为父组件在状态值更新之前已经渲染完成。相比之下,在客户端(当启用 CSR 时,这是默认设置)值将被传播,并且层次结构中更高的组件、页面和布局将响应新值。因此,为了避免在数据同步过程中状态更新时值“闪烁”,通常建议将状态向下传递到组件,而不是向上传递。 如果您不使用 SSR(并且可以保证未来不会需要使用 SSR)的话,那么您可以在共享模块中安全地保留状态,而不需要使用上下文 API。 ## 组件和页面状态已保留 当你浏览应用程序时,SvelteKit 会重用现有的布局和页面组件。例如,如果你有一个这样的路由... ```svelteReading time: {Math.round(estimatedReadingTime)} minutes