Home > Web Front-end > CSS Tutorial > Caching Data in SvelteKit

Caching Data in SvelteKit

尊渡假赌尊渡假赌尊渡假赌
Release: 2025-03-09 11:18:13
Original
372 people have browsed it

Caching Data in SvelteKit

My previous post provided a general overview of SvelteKit's capabilities. This post delves into a crucial aspect of web development: caching. If you haven't already, please read my previous post for context. The complete code and a live demo are available on GitHub.

This post focuses on efficient data handling. We'll implement basic search functionality that modifies the page's query string (using SvelteKit's built-in features) and re-triggers the page's loader. Instead of repeatedly querying a database, we'll leverage caching to quickly retrieve previously searched data. We'll explore techniques for managing cache expiration and, critically, manual cache invalidation. Finally, we'll demonstrate how to update data client-side after a mutation while simultaneously clearing the cache.

This is a more advanced topic than usual. We'll implement features similar to those found in libraries like react-query, but without relying on external dependencies. We'll only use web platform APIs and SvelteKit features.

While the web platform's features are lower-level, requiring more manual effort, the benefit is reduced bundle size due to the absence of external libraries. However, only use these techniques when absolutely necessary. Caching can be complex and easily mismanaged. If your data store and UI are performant enough, let SvelteKit handle data fetching directly – simplicity is key. This post provides solutions for when that's no longer sufficient.

Note that react-query now has Svelte support! If you frequently require manual caching, consider exploring this library.

Setup

We'll modify our previous code to illustrate additional SvelteKit features.

First, let's move data loading from the page.server.js loader to an API route. Create a server.js file in routes/api/todos and add a GET function:

import { json } from "@sveltejs/kit";
import { getTodos } from "$lib/data/todoData";

export async function GET({ url, setHeaders, request }) {
  const search = url.searchParams.get("search") || "";
  setHeaders({ "cache-control": "max-age=60" }); // Add caching header
  const todos = await getTodos(search);
  return json(todos);
}
Copy after login
Copy after login
Copy after login

Next, rename the page loader from page.server.js to page.js (or .ts). This makes it a universal loader, running on both server and client. The client-side fetch will use the browser's native fetch function.

export async function load({ fetch, url, setHeaders }) {
  const search = url.searchParams.get("search") || "";
  const resp = await fetch(`/api/todos?search=${encodeURIComponent(search)}`);
  const todos = await resp.json();
  return { todos };
}
Copy after login
Copy after login

Add a simple search form to the /list page:

<div>
  <label for="search">Search:</label>
  <input type="text" id="search" bind:value="{searchQuery}" on:keypress="{(e)"> { if (e.key === 'Enter') { navigate(`/list?search=${encodeURIComponent(searchQuery)}`) } }}>
</div>
Copy after login
Copy after login

Now, entering a search term will update the URL's query string, triggering the loader and searching to-do items.

Finally, increase the delay in todoData.js to easily observe caching behavior:

export const wait = async (amount) => new Promise((res) => setTimeout(res, amount ?? 500));
Copy after login
Copy after login

The full code is on GitHub.

Basic Caching

Let's add caching to our /api/todos endpoint by modifying the server.js file:

import { json } from "@sveltejs/kit";
import { getTodos } from "$lib/data/todoData";

export async function GET({ url, setHeaders, request }) {
  const search = url.searchParams.get("search") || "";
  setHeaders({ "cache-control": "max-age=60" }); // Add caching header
  const todos = await getTodos(search);
  return json(todos);
}
Copy after login
Copy after login
Copy after login

This caches API calls for 60 seconds. Adjust this value as needed. Consider stale-while-revalidate for more sophisticated caching strategies.

Now, subsequent searches within the 60-second window will load instantly from the cache. Remember to disable caching in your browser's developer tools to observe the caching behavior.

Cache Location

The initial server-rendered load is fetched on the server and sent to the client. SvelteKit observes the Cache-Control header and uses cached data within the specified timeframe. Client-side searches will use the browser's cache, potentially persisting even after page reloads (depending on cache-busting implementation).

Cache Invalidation

To manually invalidate the cache, we'll add a query-busting value to the URL. We'll store this value in a cookie, settable on the server but readable on the client.

Create a layout.server.js file at the root of your routes folder:

export async function load({ fetch, url, setHeaders }) {
  const search = url.searchParams.get("search") || "";
  const resp = await fetch(`/api/todos?search=${encodeURIComponent(search)}`);
  const todos = await resp.json();
  return { todos };
}
Copy after login
Copy after login

This sets a cookie on the initial request and reads it on subsequent requests. httpOnly: false allows client-side access for cache-busting purposes.

Reading Cache Values

Our universal loader needs to read this cache value regardless of execution environment. On the client, we'll parse document.cookie:

<div>
  <label for="search">Search:</label>
  <input type="text" id="search" bind:value="{searchQuery}" on:keypress="{(e)"> { if (e.key === 'Enter') { navigate(`/list?search=${encodeURIComponent(searchQuery)}`) } }}>
</div>
Copy after login
Copy after login

Sending Cache Value

Now, send this value to the /api/todos endpoint:

export const wait = async (amount) => new Promise((res) => setTimeout(res, amount ?? 500));
Copy after login
Copy after login

Busting the Cache

Update the cache-busting value in any server action using:

setHeaders({ "cache-control": "max-age=60" });
Copy after login

Implementation

We've covered the necessary primitives. Now, let's integrate them. We'll add editing functionality to the /list page. Add these rows to your table:

export function load({ cookies, isDataRequest }) {
  const initialRequest = !isDataRequest;
  const cacheValue = initialRequest ? +new Date() : cookies.get("todos-cache");
  if (initialRequest) {
    cookies.set("todos-cache", cacheValue, { path: "/", httpOnly: false });
  }
  return { todosCacheBust: cacheValue };
}
Copy after login

Create a page.server.js file in the /list folder:

export function getCookieLookup() {
  if (typeof document !== "object") {
    return {};
  }
  return document.cookie.split("; ").reduce((lookup, v) => {
    const parts = v.split("=");
    lookup[parts[0]] = parts[1];
    return lookup;
  }, {});
}

export const getCurrentCookieValue = (name) => {
  const cookies = getCookieLookup();
  return cookies[name] ?? "";
};
Copy after login

This handles form submissions, updates the to-do item, and clears the cache.

After editing, a fetch to /todos retrieves the updated data.

Immediate Updates

To avoid the post-edit fetch, update the UI directly. Modify the loader to return a writable store:

import { getCurrentCookieValue } from "$lib/util/cookieUtils";

export async function load({ fetch, parent, url, setHeaders }) {
  const parentData = await parent();
  const cacheBust = getCurrentCookieValue("todos-cache") || parentData.todosCacheBust;
  const search = url.searchParams.get("search") || "";
  const resp = await fetch(`/api/todos?search=${encodeURIComponent(search)}&cache=${cacheBust}`);
  const todos = await resp.json();
  return { todos };
}
Copy after login

Use $todos in your template: {#each $todos as t}. Now, update the store directly:

cookies.set("todos-cache", +new Date(), { path: "/", httpOnly: false });
Copy after login

This updates the store without triggering a re-render. The cache is cleared, and changes are immediately visible.

The code for immediate updates is on GitHub.

Reload Function

Let's add a reload button to clear the cache and reload the current query. Add a server action:

<tr>
  <td><input type="text" bind:value="{t.title}" name="title"></td>
  <td><input type="hidden" name="id" value="{t.id}"></td>
  <td><button type="submit" form="edit-{t.id}">Save</button></td>
</tr>
Copy after login

And a form:

import { getTodo, updateTodo, wait } from "$lib/data/todoData";

export const actions = {
  async editTodo({ request, cookies }) {
    const formData = await request.formData();
    const id = formData.get("id");
    const newTitle = formData.get("title");
    await wait(250);
    updateTodo(id, newTitle);
    cookies.set("todos-cache", +new Date(), { path: "/", httpOnly: false });
  },
};
Copy after login

To improve this, let's add feedback and control invalidation:

return { todos: writable(todos) };
Copy after login

This uses invalidate to control what's reloaded, preventing unnecessary re-renders. Update the loader:

import { json } from "@sveltejs/kit";
import { getTodos } from "$lib/data/todoData";

export async function GET({ url, setHeaders, request }) {
  const search = url.searchParams.get("search") || "";
  setHeaders({ "cache-control": "max-age=60" }); // Add caching header
  const todos = await getTodos(search);
  return json(todos);
}
Copy after login
Copy after login
Copy after login

The code for the reload button is on GitHub.

Conclusion

This post explored advanced caching techniques in SvelteKit. Remember to use these only when necessary. Prioritize clean, simple code and optimize only when performance becomes an issue. The goal was to provide tools for when optimization is truly needed.

The above is the detailed content of Caching Data in SvelteKit. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template