เอ็กซ์เลร่า8

การแคชข้อมูลใน SvelteKit

My โพสต์ก่อนหน้านี้ เป็นภาพรวมกว้างๆ ของ SvelteKit ซึ่งเราเห็นว่าเป็นเครื่องมือที่ยอดเยี่ยมสำหรับการพัฒนาเว็บ โพสต์นี้จะแยกสิ่งที่เราทำที่นั่นและดำดิ่งสู่หัวข้อโปรดของนักพัฒนาทุกคน: แคช. ดังนั้นอย่าลืมอ่านโพสต์ล่าสุดของฉันหากคุณยังไม่ได้อ่าน รหัสสำหรับโพสต์นี้ มีอยู่ใน GitHubเช่นเดียวกับ การสาธิตสด.

โพสต์นี้เกี่ยวกับการจัดการข้อมูล เราจะเพิ่มฟังก์ชันการค้นหาพื้นฐานที่จะแก้ไขสตริงข้อความค้นหาของเพจ (โดยใช้ฟีเจอร์ SvelteKit ในตัว) และเรียกใช้ตัวโหลดของเพจอีกครั้ง แต่แทนที่จะเพียงแค่ค้นหาฐานข้อมูล (จินตภาพ) ของเราอีกครั้ง เราจะเพิ่มการแคช ดังนั้น การค้นหาการค้นหาก่อนหน้าซ้ำ (หรือใช้ปุ่มย้อนกลับ) จะแสดงข้อมูลที่เรียกค้นก่อนหน้านี้อย่างรวดเร็วจากแคช เราจะดูวิธีควบคุมระยะเวลาที่ข้อมูลแคชยังคงใช้ได้ และที่สำคัญกว่านั้น วิธีทำให้ค่าที่แคชทั้งหมดใช้ไม่ได้ด้วยตนเอง เราจะดูว่าเราสามารถอัปเดตข้อมูลด้วยตนเองบนหน้าจอปัจจุบัน ฝั่งไคลเอ็นต์ได้อย่างไร หลังจากการกลายพันธุ์ ในขณะที่ยังคงล้างแคช

นี่จะเป็นโพสต์ที่ยาวและยากกว่าโพสต์ส่วนใหญ่ที่ฉันมักจะเขียน เนื่องจากเรากำลังพูดถึงหัวข้อที่ยากกว่า โพสต์นี้จะแสดงให้คุณเห็นถึงวิธีการใช้คุณสมบัติทั่วไปของยูทิลิตี้ข้อมูลยอดนิยมเช่น โต้ตอบแบบสอบถาม; แต่แทนที่จะดึงไลบรารีภายนอกมาใช้ เราจะใช้เฉพาะแพลตฟอร์มเว็บและฟีเจอร์ SvelteKit เท่านั้น

น่าเสียดายที่ฟีเจอร์ของแพลตฟอร์มเว็บมีระดับที่ต่ำกว่าเล็กน้อย ดังนั้นเราจะทำงานให้มากกว่าที่คุณคุ้นเคย ข้อดีคือเราไม่จำเป็นต้องมีไลบรารีภายนอก ซึ่งจะช่วยให้บันเดิลมีขนาดที่ดีและมีขนาดเล็ก โปรดอย่าใช้วิธีการที่ฉันจะแสดงให้คุณเห็นเว้นแต่คุณจะมีเหตุผลที่ดี การแคชนั้นผิดพลาดได้ง่าย และอย่างที่คุณเห็น มีความซับซ้อนเล็กน้อยที่จะส่งผลให้รหัสแอปพลิเคชันของคุณ หวังว่าที่เก็บข้อมูลของคุณจะเร็ว และ UI ของคุณก็ใช้ได้ ทำให้ SvelteKit สามารถร้องขอข้อมูลที่จำเป็นสำหรับหน้าใดก็ได้เสมอ ถ้าเป็นเช่นนั้นให้ปล่อยไว้ตามลำพัง เพลิดเพลินไปกับความเรียบง่าย แต่โพสต์นี้จะแสดงให้คุณเห็นเทคนิคบางอย่างเมื่อสิ่งนั้นไม่เป็นเช่นนั้น

พูดถึงการโต้ตอบแบบสอบถามนั้น เพิ่งได้รับการปล่อยตัว สำหรับ Svelte! ดังนั้นหากคุณพบว่าตัวเองใช้เทคนิคการแคชด้วยตนเอง มากอย่าลืมตรวจสอบโครงการนั้นและดูว่าอาจช่วยได้หรือไม่

การตั้งค่า

ก่อนที่เราจะเริ่ม มาทำการเปลี่ยนแปลงเล็กน้อยกับ รหัสที่เรามีก่อนหน้านี้. สิ่งนี้จะทำให้เรามีข้ออ้างในการดูคุณสมบัติอื่นๆ ของ SvelteKit และที่สำคัญกว่านั้น ทำให้เราพร้อมสำหรับความสำเร็จ

ขั้นแรก ให้ย้ายการโหลดข้อมูลของเราจากตัวโหลดเข้ามา +page.server.js เพื่อ เส้นทาง API. เราจะสร้าง +server.js แฟ้มที่อยู่ใน routes/api/todosแล้วเพิ่ม ก GET การทำงาน. ซึ่งหมายความว่าตอนนี้เราจะสามารถดึงข้อมูล (โดยใช้คำกริยา GET เริ่มต้น) ไปยัง /api/todos เส้นทาง. เราจะเพิ่มโค้ดโหลดข้อมูลเหมือนเดิม

import { json } from "@sveltejs/kit";
import { getTodos } from "$lib/data/todoData"; export async function GET({ url, setHeaders, request }) { const search = url.searchParams.get("search") || ""; const todos = await getTodos(search); return json(todos);
}

ต่อไป เราจะใช้ตัวโหลดหน้าเว็บที่เรามี และเพียงแค่เปลี่ยนชื่อไฟล์จาก +page.server.js ไปยัง +page.js (หรือ .ts หากคุณได้โครงร่างโครงการของคุณเพื่อใช้ TypeScript) สิ่งนี้จะเปลี่ยนตัวโหลดของเราให้เป็นตัวโหลด "สากล" แทนที่จะเป็นตัวโหลดเซิร์ฟเวอร์ เอกสาร SvelteKit อธิบายความแตกต่างแต่ตัวโหลดสากลทำงานทั้งบนเซิร์ฟเวอร์และไคลเอนต์ ข้อดีอย่างหนึ่งสำหรับเราก็คือ fetch การโทรไปยังจุดสิ้นสุดใหม่ของเราจะเรียกใช้ทันทีจากเบราว์เซอร์ของเรา (หลังจากโหลดครั้งแรก) โดยใช้เบราว์เซอร์เนทีฟ fetch การทำงาน. เราจะเพิ่มการแคช HTTP มาตรฐานในอีกเล็กน้อย แต่สำหรับตอนนี้ สิ่งที่เราจะทำคือเรียกจุดสิ้นสุด

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, };
}

ตอนนี้ขอเพิ่มรูปแบบง่ายๆของเรา /list หน้า:

<div class="search-form"> <form action="/th/list"> <label>Search</label> <input autofocus name="search" /> </form>
</div>

ได้ แบบฟอร์มสามารถกำหนดเป้าหมายไปยังตัวโหลดหน้าปกติของเราได้โดยตรง ตอนนี้เราสามารถเพิ่มคำค้นหาในช่องค้นหาได้แล้ว กด เข้าสู่และคำ "ค้นหา" จะต่อท้ายสตริงข้อความค้นหาของ URL ซึ่งจะเรียกใช้ตัวโหลดของเราอีกครั้งและค้นหารายการสิ่งที่ต้องทำของเรา

แบบฟอร์มการค้นหา

มาเพิ่มความล่าช้าในของเราด้วย todoData.js แฟ้มที่อยู่ใน /lib/data. สิ่งนี้จะทำให้ง่ายต่อการดูเมื่อข้อมูลถูกและไม่ถูกแคชในขณะที่เราดำเนินการผ่านโพสต์นี้

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

โปรดจำไว้ว่ารหัสเต็มสำหรับโพสต์นี้คือ ทั้งหมดบน GitHubถ้าคุณต้องการอ้างอิง

แคชพื้นฐาน

เริ่มต้นด้วยการเพิ่มแคชให้กับเรา /api/todos จุดสิ้นสุด เราจะกลับไปที่ของเรา +server.js ไฟล์และเพิ่มส่วนหัวการควบคุมแคชแรกของเรา

setHeaders({ "cache-control": "max-age=60",
});

…ซึ่งจะทำให้ฟังก์ชันทั้งหมดมีลักษณะดังนี้:

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

เราจะตรวจสอบการทำให้ไม่ถูกต้องด้วยตนเองในไม่ช้า แต่ฟังก์ชันทั้งหมดนี้บอกว่าจะแคชการเรียก API เหล่านี้เป็นเวลา 60 วินาที ตั้งค่านี้เป็นสิ่งที่คุณต้องการและขึ้นอยู่กับกรณีการใช้งานของคุณ stale-while-revalidate อาจคุ้มค่าที่จะดู

และเช่นเดียวกัน แบบสอบถามของเรากำลังแคช

แคชใน DevTools

หมายเหตุ ทำให้แน่ใจว่าคุณ ยกเลิกการเลือก ช่องทำเครื่องหมายที่ปิดใช้งานการแคชในเครื่องมือ dev

โปรดจำไว้ว่า หากการนำทางเริ่มต้นของคุณในแอปคือหน้ารายการ ผลการค้นหาเหล่านั้นจะถูกแคชไว้ภายใน SvelteKit ดังนั้นอย่าคาดหวังว่าจะเห็นสิ่งใดใน DevTools เมื่อกลับไปที่การค้นหานั้น

แคชอะไรและที่ใด

โหลดแอปของเราที่แสดงผลโดยเซิร์ฟเวอร์เป็นครั้งแรก (สมมติว่าเราเริ่มต้นที่ไฟล์ /list หน้า) จะถูกเรียกบนเซิร์ฟเวอร์ SvelteKit จะทำการซีเรียลไลซ์และส่งข้อมูลนี้ไปยังลูกค้าของเรา ยิ่งไปกว่านั้นก็จะคอยสังเกตการ Cache-Control ส่วนหัว ในการตอบกลับ และจะทราบว่าจะใช้ข้อมูลแคชนี้สำหรับการเรียกปลายทางนั้นภายในหน้าต่างแคช (ซึ่งเราตั้งค่าไว้ที่ 60 วินาทีในตัวอย่างที่ใส่)

หลังจากการโหลดครั้งแรกนั้น เมื่อคุณเริ่มค้นหาบนเพจ คุณควรเห็นคำขอเครือข่ายจากเบราว์เซอร์ของคุณไปยัง /api/todos รายการ. เมื่อคุณค้นหาสิ่งที่คุณค้นหาแล้ว (ภายใน 60 วินาทีที่ผ่านมา) คำตอบควรโหลดทันทีเนื่องจากถูกแคชไว้

สิ่งที่เจ๋งเป็นพิเศษสำหรับแนวทางนี้คือ เนื่องจากนี่เป็นการแคชผ่านแคชแบบเนทีฟของเบราว์เซอร์ การเรียกใช้เหล่านี้อาจ (ขึ้นอยู่กับวิธีที่คุณจัดการการหยุดแคชที่เราจะดู) แคชต่อไปแม้ว่าคุณจะโหลดหน้าซ้ำ (ไม่เหมือน โหลดฝั่งเซิร์ฟเวอร์เริ่มต้น ซึ่งเรียกจุดสิ้นสุดใหม่เสมอ แม้ว่าจะทำภายใน 60 วินาทีที่ผ่านมาก็ตาม)

เห็นได้ชัดว่าข้อมูลสามารถเปลี่ยนแปลงได้ตลอดเวลา ดังนั้นเราจึงต้องการวิธีล้างแคชนี้ด้วยตนเอง ซึ่งเราจะพิจารณาในครั้งต่อไป

แคชใช้ไม่ได้

ตอนนี้ข้อมูลจะถูกแคชเป็นเวลา 60 วินาที ไม่ว่าจะเกิดอะไรขึ้น หลังจากผ่านไปหนึ่งนาที ข้อมูลใหม่จะถูกดึงออกจากที่เก็บข้อมูลของเรา คุณอาจต้องการระยะเวลาที่สั้นลงหรือยาวขึ้น แต่จะเกิดอะไรขึ้นหากคุณเปลี่ยนข้อมูลบางส่วนและต้องการล้างแคชของคุณทันที เพื่อให้การค้นหาครั้งต่อไปของคุณเป็นปัจจุบัน เราจะแก้ปัญหานี้โดยเพิ่มค่าป้องกันการสืบค้นให้กับ URL ที่เราส่งไปยัง URL ใหม่ของเรา /todos จุดสิ้นสุด

มาเก็บค่าที่แคชทำลายนี้ไว้ในคุกกี้กันเถอะ ค่านั้นสามารถตั้งค่าบนเซิร์ฟเวอร์ แต่ยังคงอ่านบนไคลเอ็นต์ มาดูตัวอย่างโค้ดกัน

เราสามารถสร้าง +layout.server.js ไฟล์ที่รูทสุดของเรา routes โฟลเดอร์ สิ่งนี้จะทำงานเมื่อเริ่มต้นแอปพลิเคชันและเป็นสถานที่ที่สมบูรณ์แบบในการตั้งค่าคุกกี้เริ่มต้น

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, };
}

คุณอาจสังเกตเห็นไฟล์ isDataRequest ค่า. โปรดจำไว้ว่าเลย์เอาต์จะเรียกใช้ใหม่ทุกครั้งที่มีการเรียกรหัสไคลเอ็นต์ invalidate()หรือเมื่อใดก็ตามที่เราเรียกใช้การดำเนินการของเซิร์ฟเวอร์ (สมมติว่าเราไม่ได้ปิดการทำงานเริ่มต้น) isDataRequest บ่งชี้ถึงการเรียกใช้ซ้ำ ดังนั้นเราจะตั้งค่าคุกกี้หากเป็นเช่นนั้นเท่านั้น false; มิฉะนั้นเราจะส่งสิ่งที่มีอยู่แล้วไปพร้อมกัน

พื้นที่ httpOnly: false ธงก็มีความสำคัญเช่นกัน สิ่งนี้ทำให้รหัสลูกค้าของเราสามารถอ่านค่าคุกกี้เหล่านี้ได้ document.cookie. โดยปกติจะเป็นข้อกังวลด้านความปลอดภัย แต่ในกรณีของเรา ตัวเลขเหล่านี้เป็นตัวเลขที่ไม่มีความหมายซึ่งช่วยให้เราสามารถแคชหรือปิดแคชได้

การอ่านค่าแคช

รถตักอเนกประสงค์ของเราคือสิ่งที่เรียกหาเรา /todos จุดสิ้นสุด สิ่งนี้ทำงานบนเซิร์ฟเวอร์หรือไคลเอ็นต์ และเราจำเป็นต้องอ่านค่าแคชที่เราเพิ่งตั้งค่าไม่ว่าเราจะอยู่ที่ไหน มันค่อนข้างง่ายถ้าเราอยู่บนเซิร์ฟเวอร์ เราสามารถโทร await parent() เพื่อรับข้อมูลจากพาเรนต์เลย์เอาต์ แต่สำหรับไคลเอ็นต์ เราจะต้องใช้โค้ดขั้นต้นในการแยกวิเคราะห์ document.cookie:

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; }, {});
} const getCurrentCookieValue = name => { const cookies = getCookieLookup(); return cookies[name] ?? "";
};

โชคดีที่เราต้องการเพียงครั้งเดียว

ส่งออกค่าแคช

แต่ตอนนี้เราต้อง ส่ง คุณค่านี้ของเรา /todos จุดสิ้นสุด

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, };
}

getCurrentCookieValue('todos-cache') มีการตรวจสอบเพื่อดูว่าเราอยู่ในไคลเอนต์หรือไม่ (โดยการตรวจสอบประเภทของเอกสาร) และไม่ส่งคืนสิ่งใดหากเราเป็นเช่นนั้น ซึ่งจุดนั้นเรารู้ว่าเราอยู่บนเซิร์ฟเวอร์ จากนั้นจะใช้ค่าจากเค้าโครงของเรา

ทำลายแคช

แต่ อย่างไร เราอัปเดตค่าการหยุดแคชนั้นจริง ๆ เมื่อเราต้องการหรือไม่ เนื่องจากมันถูกเก็บไว้ในคุกกี้ เราจึงสามารถเรียกสิ่งนี้ได้จากการกระทำใดๆ ของเซิร์ฟเวอร์:

cookies.set("todos-cache", cacheValue, { path: "/", httpOnly: false });

การนำไปใช้งาน

ทุกอย่างตกต่ำจากที่นี่ เราได้ทำงานหนัก เราได้กล่าวถึงแพลตฟอร์มเว็บพื้นฐานต่างๆ ที่เราต้องการ รวมถึงที่มาที่ไป ตอนนี้มาสนุกกันและเขียนโค้ดแอปพลิเคชันเพื่อเชื่อมโยงทั้งหมดเข้าด้วยกัน

สำหรับเหตุผลที่จะชัดเจนในไม่ช้า เรามาเริ่มด้วยการเพิ่มฟังก์ชันการแก้ไขให้กับเรา /list หน้าหนังสือ. เราจะเพิ่มแถวที่สองของตารางสำหรับแต่ละสิ่งที่ต้องทำ:

import { enhance } from "$app/forms";
<tr> <td colspan="4"> <form use:enhance method="post" action="?/editTodo"> <input name="id" value="{t.id}" type="hidden" /> <input name="title" value="{t.title}" /> <button>Save</button> </form> </td>
</tr>

และแน่นอน เราจะต้องเพิ่มการดำเนินการกับฟอร์มของเรา /list หน้าหนังสือ. การกระทำสามารถเข้าไปได้เท่านั้น .server หน้า ดังนั้นเราจะเพิ่ม +page.server.js ในของเรา /list โฟลเดอร์ (ใช่ +page.server.js ไฟล์สามารถอยู่ร่วมกันถัดจากไฟล์ +page.js ไฟล์.)

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 }); },
};

เรากำลังดึงข้อมูลในแบบฟอร์ม บังคับให้เกิดความล่าช้า อัปเดตสิ่งที่ต้องทำ และที่สำคัญที่สุดคือการล้างแคชคุกกี้ของเรา

ลองมาลองดูกัน โหลดหน้าของคุณใหม่ จากนั้นแก้ไขหนึ่งในรายการสิ่งที่ต้องทำ คุณควรเห็นการอัปเดตค่าตารางหลังจากนั้นสักครู่ หากคุณดูในแท็บเครือข่ายใน DevToold คุณจะเห็นการดึงข้อมูลไปที่ /todos จุดสิ้นสุด ซึ่งจะส่งคืนข้อมูลใหม่ของคุณ เรียบง่ายและใช้งานได้ตามค่าเริ่มต้น

กำลังบันทึกข้อมูล

อัพเดททันที

จะเป็นอย่างไรหากเราต้องการหลีกเลี่ยงการดึงข้อมูลที่เกิดขึ้นหลังจากที่เราอัปเดตรายการสิ่งที่ต้องทำ และอัปเดตรายการที่แก้ไขบนหน้าจอแทน

นี่ไม่ใช่แค่เรื่องของประสิทธิภาพเท่านั้น หากคุณค้นหา "โพสต์" แล้วลบคำว่า "โพสต์" ออกจากสิ่งที่ต้องทำใดๆ ในรายการ ข้อความเหล่านั้นจะหายไปจากรายการหลังจากการแก้ไข เนื่องจากไม่อยู่ในผลการค้นหาของหน้านั้นอีกต่อไป คุณสามารถทำให้ UX ดีขึ้นได้ด้วยแอนิเมชั่นที่มีรสนิยมสำหรับการออกจากสิ่งที่ต้องทำ แต่สมมติว่าเราต้องการ ไม่ เรียกใช้ฟังก์ชันโหลดของหน้านั้นอีกครั้ง แต่ยังคงล้างแคชและอัปเดตสิ่งที่ต้องทำที่แก้ไขเพื่อให้ผู้ใช้สามารถดูการแก้ไขได้ SvelteKit ทำให้เป็นไปได้ — มาดูกันเลย!

ขั้นแรก มาทำการเปลี่ยนแปลงเล็กน้อยกับตัวโหลดของเรา แทนที่จะส่งคืนรายการสิ่งที่ต้องทำ เรากลับมา ร้านค้าที่เขียนได้ ประกอบด้วยสิ่งที่ต้องทำของเรา

return { todos: writable(todos),
};

ก่อนหน้านี้ เราเข้าถึงสิ่งที่ต้องทำบน data prop ซึ่งเราไม่ได้เป็นเจ้าของและไม่สามารถอัปเดตได้ แต่ Svelte ให้เราส่งคืนข้อมูลของเราในร้านค้าของพวกเขาเอง (สมมติว่าเราใช้ตัวโหลดสากล ซึ่งเราเป็นอย่างนั้น) เราเพียงแค่ต้องทำการปรับแต่งเพิ่มเติมเพื่อของเรา /list หน้า.

แทนสิ่งนี้:

{#each todos as t}

…เราต้องทำเช่นนี้ตั้งแต่ todos ตอนนี้เป็นร้านค้าแล้ว:

{#each $todos as t}

ตอนนี้ข้อมูลของเราโหลดเหมือนเดิม แต่ตั้งแต่ todos เป็นร้านที่เขียนได้ เราอัพได้

ขั้นแรก ให้จัดเตรียมฟังก์ชันให้กับเรา use:enhance แอตทริบิวต์:

<form use:enhance={executeSave} on:submit={runInvalidate} method="post" action="?/editTodo"
>

สิ่งนี้จะทำงานก่อนการส่ง มาเขียนกันต่อไป:

function executeSave({ data }) { const id = data.get("id"); const title = data.get("title"); return async () => { todos.update(list => list.map(todo => { if (todo.id == id) { return Object.assign({}, todo, { title }); } else { return todo; } }) ); };
}

ฟังก์ชันนี้จัดเตรียม data คัดค้านด้วยข้อมูลแบบฟอร์มของเรา เรา กลับ ฟังก์ชัน async ที่จะทำงาน หลังจาก การแก้ไขของเราเสร็จสิ้นแล้ว เอกสาร อธิบายทั้งหมดนี้ แต่ด้วยการทำเช่นนี้ เราจะปิดการจัดการแบบฟอร์มเริ่มต้นของ SvelteKit ซึ่งจะเรียกใช้ตัวโหลดของเราอีกครั้ง นี่คือสิ่งที่เราต้องการ! (เราสามารถเรียกพฤติกรรมเริ่มต้นนั้นกลับมาได้อย่างง่ายดายตามที่เอกสารอธิบายไว้)

ตอนนี้เราโทร update เกี่ยวกับเรา todos อาร์เรย์เนื่องจากเป็นร้านค้า และนั่นก็คือ หลังจากแก้ไขรายการสิ่งที่ต้องทำ การเปลี่ยนแปลงของเราจะแสดงขึ้นทันทีและแคชของเราจะถูกล้าง (เช่นเดิม เนื่องจากเราตั้งค่าคุกกี้ใหม่ใน editTodo การกระทำในรูปแบบ) ดังนั้น หากเราค้นหาแล้วกลับไปที่หน้านี้ เราจะได้รับข้อมูลใหม่จากตัวโหลดของเรา ซึ่งจะไม่รวมรายการสิ่งที่ต้องทำที่อัปเดตซึ่งได้รับการอัปเดตอย่างถูกต้อง

รหัสสำหรับการปรับปรุงทันที มีให้ที่ GitHub.

ขุดลึก

เราสามารถตั้งค่าคุกกี้ในฟังก์ชันโหลดเซิร์ฟเวอร์ (หรือการทำงานของเซิร์ฟเวอร์) ไม่ใช่แค่เค้าโครงรูท ดังนั้น หากมีการใช้ข้อมูลบางส่วนภายใต้เลย์เอาต์เดียว หรือแม้แต่หน้าเดียว คุณสามารถตั้งค่าคุกกี้ตรงนั้นได้ ยิ่งไปกว่านั้น หากคุณเป็น ไม่ ทำตามเคล็ดลับที่ฉันเพิ่งแสดงการอัปเดตข้อมูลบนหน้าจอด้วยตนเอง และต้องการให้ตัวโหลดของคุณทำงานอีกครั้งหลังจากการกลายพันธุ์ คุณสามารถตั้งค่าคุกกี้ใหม่ได้ตลอดเวลาในฟังก์ชันการโหลดนั้นโดยไม่ต้องตรวจสอบใดๆ isDataRequest. ระบบจะตั้งค่าเริ่มต้น จากนั้นเมื่อใดก็ตามที่คุณเรียกใช้การดำเนินการของเซิร์ฟเวอร์ที่โครงร่างเพจจะทำให้ใช้งานไม่ได้โดยอัตโนมัติและเรียกตัวโหลดของคุณใหม่ ตั้งค่าสตริงที่ปิดแคชใหม่ก่อนที่จะเรียกตัวโหลดสากลของคุณ

การเขียนฟังก์ชันรีโหลด

มาปิดท้ายด้วยการสร้างฟีเจอร์สุดท้าย: ปุ่มรีโหลด ให้ผู้ใช้มีปุ่มที่จะล้างแคชแล้วโหลดแบบสอบถามปัจจุบันอีกครั้ง

เราจะเพิ่มการดำเนินการในรูปแบบเรียบง่ายของสิ่งสกปรก:

async reloadTodos({ cookies }) { cookies.set('todos-cache', +new Date(), { path: '/', httpOnly: false });
},

ในโครงการจริง คุณคงไม่คัดลอก/วางโค้ดเดียวกันเพื่อตั้งค่าคุกกี้เดียวกันในลักษณะเดียวกันในหลายๆ ที่ แต่สำหรับโพสต์นี้ เราจะปรับให้เรียบง่ายและอ่านง่ายที่สุด

ตอนนี้มาสร้างแบบฟอร์มเพื่อโพสต์:

<form method="POST" action="?/reloadTodos" use:enhance> <button>Reload todos</button>
</form>

ได้ผล!

UI หลังจากโหลดซ้ำ

เราสามารถเรียกสิ่งนี้ว่าเสร็จแล้วไปต่อได้ แต่มาปรับปรุงวิธีแก้ปัญหานี้กันสักหน่อย โดยเฉพาะอย่างยิ่ง เรามาแสดงความคิดเห็นบนหน้าเพื่อบอกผู้ใช้ว่าการโหลดซ้ำกำลังเกิดขึ้น นอกจากนี้ ตามค่าเริ่มต้น การดำเนินการของ SvelteKit จะไม่ถูกต้อง ทุกอย่าง. ทุกเค้าโครง หน้า ฯลฯ ในลำดับชั้นของหน้าปัจจุบันจะโหลดซ้ำ อาจมีข้อมูลบางอย่างที่โหลดเพียงครั้งเดียวในเลย์เอาต์รูทที่เราไม่จำเป็นต้องทำให้ไม่ถูกต้องหรือโหลดซ้ำ

ดังนั้น เรามาโฟกัสกันสักหน่อย และโหลดสิ่งที่ต้องทำของเราใหม่เมื่อเราเรียกใช้ฟังก์ชันนี้เท่านั้น

ขั้นแรก ให้ส่งฟังก์ชันเพื่อปรับปรุง:

<form method="POST" action="?/reloadTodos" use:enhance={reloadTodos}>
import { enhance } from "$app/forms";
import { invalidate } from "$app/navigation"; let reloading = false;
const reloadTodos = () => { reloading = true; return async () => { invalidate("reload:todos").then(() => { reloading = false; }); };
};

เรากำลังตั้งค่าใหม่ reloading ตัวแปรถึง true ที่ เริ่มต้น ของการกระทำนี้ จากนั้น เพื่อแทนที่พฤติกรรมเริ่มต้นของการทำให้ทุกอย่างเป็นโมฆะ เราจะคืนค่า an async การทำงาน. ฟังก์ชันนี้จะทำงานเมื่อการดำเนินการของเซิร์ฟเวอร์ของเราเสร็จสิ้น (ซึ่งเพิ่งตั้งค่าคุกกี้ใหม่)

หากไม่มีสิ่งนี้ async ส่งคืนฟังก์ชัน SvelteKit จะทำให้ทุกอย่างเป็นโมฆะ เนื่องจากเราให้บริการฟังก์ชันนี้ จะไม่มีสิ่งใดใช้ไม่ได้ ดังนั้นจึงขึ้นอยู่กับเราที่จะบอกว่าควรโหลดอะไรใหม่ เราทำสิ่งนี้กับ invalidate การทำงาน. เราเรียกมันว่ามีค่าเป็น reload:todos. ฟังก์ชันนี้ส่งคืนคำสัญญา ซึ่งจะแก้ไขเมื่อการยกเลิกใช้งานเสร็จสมบูรณ์ ณ จุดที่เราตั้งไว้ reloading กลับไป false.

สุดท้าย เราต้องซิงค์ตัวโหลดของเรากับตัวใหม่นี้ reload:todos ค่าไม่ถูกต้อง เราทำเช่นนั้นในตัวโหลดของเราด้วย depends ฟังก์ชั่น:

export async function load({ fetch, url, setHeaders, depends }) { depends('reload:todos'); // rest is the same

และนั่นก็คือ depends และ invalidate เป็นฟังก์ชันที่มีประโยชน์อย่างเหลือเชื่อ สิ่งที่เจ๋งก็คือ invalidate ไม่เพียงแค่ใช้ค่าตามอำเภอใจที่เราให้เหมือนที่เราทำ เรายังสามารถระบุ URL ซึ่ง SvelteKit จะติดตาม และทำให้ตัวโหลดใดๆ ที่ขึ้นอยู่กับ URL นั้นใช้ไม่ได้ ด้วยเหตุนี้ หากคุณสงสัยว่าเราจะข้ามการโทรไปที่ depends และทำให้ของเราเป็นโมฆะ /api/todos endpoint พร้อมกันได้ แต่คุณต้องระบุ แน่นอน URL รวมทั้ง search ระยะ (และค่าแคชของเรา) ดังนั้น คุณสามารถรวม URL สำหรับการค้นหาปัจจุบัน หรือจับคู่กับชื่อพาธ เช่นนี้:

invalidate(url => url.pathname == "/api/todos");

โดยส่วนตัวแล้วฉันพบวิธีแก้ปัญหาที่ใช้ depends ชัดเจนและเรียบง่ายยิ่งขึ้น แต่ดู เอกสาร สำหรับข้อมูลเพิ่มเติม แน่นอน และตัดสินใจด้วยตัวคุณเอง

หากคุณต้องการดูการทำงานของปุ่มโหลดซ้ำ รหัสสำหรับปุ่มนั้นอยู่ในนั้น สาขานี้ของการซื้อคืน.

พรากจากความคิด

นี่เป็นโพสต์ที่ยาว แต่หวังว่าจะไม่ล้นหลาม เราใช้หลายวิธีในการแคชข้อมูลเมื่อใช้ SvelteKit สิ่งเหล่านี้ส่วนใหญ่เป็นเรื่องของการใช้แพลตฟอร์มเว็บดั้งเดิมเพื่อเพิ่มแคชและค่าคุกกี้ที่ถูกต้อง ความรู้ที่จะให้บริการคุณในการพัฒนาเว็บโดยทั่วไป นอกเหนือจาก SvelteKit

ยิ่งไปกว่านั้นสิ่งนี้เป็นสิ่งที่คุณแน่นอน ไม่จำเป็นต้องตลอดเวลา. คุณควรเข้าถึงคุณสมบัติขั้นสูงเหล่านี้เมื่อคุณเท่านั้น ต้องการพวกเขาจริงๆ. หากที่เก็บข้อมูลของคุณให้บริการข้อมูลอย่างรวดเร็วและมีประสิทธิภาพ และคุณไม่ได้จัดการกับปัญหาการปรับสเกลใดๆ ก็ไม่มีเหตุผลที่จะทำให้โค้ดแอปพลิเคชันของคุณขยายใหญ่ขึ้นด้วยความซับซ้อนโดยไม่จำเป็นในการทำสิ่งที่เราพูดถึงที่นี่

และเช่นเคย เขียนโค้ดที่ชัดเจน สะอาดตา และเรียบง่าย และเพิ่มประสิทธิภาพเมื่อจำเป็น จุดประสงค์ของโพสต์นี้คือเพื่อมอบเครื่องมือเพิ่มประสิทธิภาพให้กับคุณเมื่อคุณต้องการจริงๆ ฉันหวังว่าคุณจะสนุกกับมัน!

แชทกับเรา

สวัสดี! ฉันจะช่วยคุณได้อย่างไร?