ã¯ããã«
ãã®è¨äºã¯ãクソアプリ - Qiita Advent Calendar 2024 - Qiitaã®7æ¥ç®ã®è¨äºã§ã ãã®ã¢ããã³ãã«ã¬ã³ãã¼ãä»å¹´ã§10å¨å¹´ã§ãããã§ããããªã¬ä»¥å¤ã®æ¯å¹´æ¸ãã¦ããããããã¡ã³ãã¼ã¯ä½äººããã®ã ãããï¼
ä»ã¾ã§ã®è¨äºã®ãªã³ã¯ ãããæ°ããããã®è¨äºãå ¥ãã¦ã¯ã½ã¢ããªã13åãããã¢ããã³ãã«ã¬ã³ãã¼ã10å¹´çå¤è³ã§åå ãã¦ããã®ã«ã3åå¤ããã©ãããæ¥å¸¸ã§ããããç©ãä½ã£ã¦ã¯ããã°ãæ¸ãã¦ãããããã1åã«ããªããªãã¨ããã®ããããããã ã
ãã¦ãé¡ããã¨10年以ä¸åã2000年代å¾ãå½æSEã ã£ãåã¯ç¾å®¹å®¤ã«åªãã¦ããå°å ã®å人ã«ãããªç´æãããã ããã¤ãèªåã®ç¾å®¹å®¤ããªã¼ãã³ãããHPãã¤ãã£ã¦ãããã
çµã¦2020å¹´é ãã¤ãã«å½¼ã¯ç¬ç«ãç¾å®¹å®¤ããªã¼ãã³ãããç´æãå®ãæãæ¥ãã®ã ã
å¶ä½è²»ãéç¨è²»ãããããªã代ããã«å¥½ãåæãããã¦ããããã¨ããã 飲é£åºãç¾å®¹å®¤ã®HPã¯å¿ è¦ãªæ å ±ã«ã¢ã¯ã»ã¹ããããããã¹ãã ã¨æã£ã¦ããã
ããããããã·ã³ãã«ãªãµã¤ãã«ãã¦ããããããªãããGoogleã®SEO対çããã¦ãããã
Googleã«å¥½ããã¤ã¤ãã·ã³ãã«ãªHPã§å¯è½ãªéã¹ãä½ç½ã¯ãªã«ãï¼
ï¼ï¼
"ã¹ãã¼ãã®åããå´"ã ã
ãªã¬ãæã«å ¥ãã¦ããã»ã»ã»ï¼ "ãã®é å"ã»ã» "ã¹ãã¼ãã®åããå´"ãã»ã»ï¼ï¼
ä»å¹´ã®ã¯ã½ã¢ããªã¯ããåããé£ãã¦è¡ã£ã¦ããã "ã¹ãã¼ãã®åããå´" ã«ãã©ï¼ï¼
â»æ³¨æ ãã®è¨äºã¯ã2000年代ã®ã¤ã³ã¿ã¼ãããã®ããªã¨é±åãã¬ã¸ã³é£è¼ããã¦ãã"ç¾é¢¨ä¼èª¬ ç¹æ»ã®æ"(ããã§ãã㤠ã¶ã£ãã¿ã®ãã) ã®èãç¥èã®å½±é¿ãåãã¦ãã¾ã
ä½ã£ããã®
é楽ã§éç¨ãã¦ããå°å ã®åã ã¡ã®ç¾å®¹å®¤ã®HP(é ããã¼ã¸ãã)ã¯ãã¡ãã
ä»åã¯ããã®HPã"ã¹ãã¼ãã®åããå´ã¸" èµ°ãæããè»è·¡ãç´¹ä»ãã¦ãããã»ã»ã»ï¼
HPã®ãã¼ã¹
Astroã使ã£ã¦éçãµã¤ããSSGãã¦ããã Astroã¯ã³ã³ãã¤ã«ãããã¨ã§ãããªãæ å ±ã諸ã åé¤ããå§ç¸®ããã表示ãæ©ããããã¨ã«ç¹åããFWã ã¾ããã¼ã¸å ã®ä¸é¨ã®JavaScriptãé 延ãã¼ããã¦åãããReactã«å¯¾å¿ãã¦ããã
UIã³ã³ãã¼ãã³ãã«Tailwindãã¼ã¹ã®daisyUIã使ç¨
Prefetchã使ããã¨ã§ããã¼ã¸å ã®ãªã³ã¯ããã£ãã·ã¥ãã¦ç»é¢é·ç§»æã«è¡¨ç¤ºæ©ãããããã«ãã¦ããã
第ä¸ä¸ä»£ã®ãããã¤å Netlify
HPã®ãã¹ãã£ã³ã°å ã¯æ £ãã¦ãã Netlifyããã
"é»è³çè¦å¨" ã«ãããã¤ãã
ããã©ã¼ãã³ã¹ã®è¨æ¸¬ããLighthouseã®çµæãè¦ã¦ãã
ããã¤ãè¦ã¦ã©ãæãï¼
ï¼ï¼
ããã大ããã§ã100ç¹æºç¹ã§ã
Netlifyã«ãã¹ãã£ã³ã°ãããã£ãã·ã¥ã使ããªãå ´åã®è¡¨ç¤ºé度ã¯ãããããã¼ã¸ã§500msç¨åº¦
ããã¾ã§ååé度ãæ©ããå¿ è¦ãªæ å ±ãªã³ã³ãã³ãã¯å ¨é¨å®è£ ãã¦ããããç»åãSVGã«ãã¦å®¹éãä¸ãã¦ããããã¾ãã«SEO対çããããå°æ¹é½å¸ã®ç¾å®¹å®¤ã®HPã«ãã¦ã¯ååã ã
ã ãå¾ ã£ã¦ã»ãããä»åã®ãã¼ãã¯"ã¹ãã¼ãã®åããå´"ã ã
ã¹ãã¼ãã®åããå´ã¸ã®æ ã¯ã¾ã å§ã¾ã£ãã°ããã ã»ã»ã»ï¼ï¼
第äºä¸ä»£ã®ãããã¤å Cloudflare
"é»è³çè¦å¨"ã«ãå¼±ç¹ããããæ¥æ¬ã«CDNãµã¼ãã®ãªã¼ã¸ã§ã³ããªãã®ã
ä¸çªè¿ãã¦ã·ã³ã¬ãã¼ã«
ããã¦éä¿¡ãããã³ã«ãHTTP/2ã«å¯¾å¿ãã¦ããã "HTTP/3" ã«ã¯å¯¾å¿ãã¦ããªã
CDNãµã¼ããæ¥æ¬ã«ãã£ã¦ãHTTP/3ã«å¯¾å¿ãã¦ããã
ããã§ãã¦Netlify並ã«éçºä½é¨ããããããã¦ç¡ææ ã大ããã¨ãã
ãã㪠"é½å" ã®è¯ããµã¼ãã¹ãæããï¼ããããããã§ã»ã»ã»ï¼
ï¼ï¼
ï¼ï¼
"é²ç¾ å®æé½ç"
"ããã¤"ã ã»ã»ã»ï¼ï¼ï¼ ãã¨ã¬ã¼ã¼ãâCOOOLâãããã»ã»ã»ï¼
Cloudflareã®Workers & Pagesã«ãããã¤ãã¦CDNããã£ãã·ã¥ããããããç¶æ ã§ãããããã¼ã¸ã«ã¢ã¯ã»ã¹ããã¨ã 30ms ï¼ï¼
ãã¡ããã¬ã¹ãã³ã¹ãããã«Cache-Controlã追å ãã¦ããããµã¼ãã«ã¢ã¯ã»ã¹ããã«ãã£ã¹ã¯ãã£ãã·ã¥å©ç¨ããå ´å㯠3msãå©ãåºãã
ã ããã¾ã ã¾ã 足ããªãã»ã»ï¼ãã£ã¨ã ã»ã»ï¼ãã£ã¨ã¹ãã¼ãã®åããå´ã¸ã»ã»ã»ï¼ï¼
è¨æ¸¬ï¼è¨æ¸¬ï¼è¨æ¸¬
ããããã¼ã¸ã«ã¯ããç¥ããæ å ±ã«ã¢ã¯ã»ã¹ãã¦Jsonãåå¾ãã¦ã«ã«ã¼ã»ã«ã表示ããReactã®ãã¼ããããã
ãã®APIãé«éåãã¦ãããï¼ï¼ï¼
ãç¥ããæ å ±ãåå¾ããWebAPIã®æ©è½
ç¾å®¹å®¤ã®ããã°ã§ããã¢ã¡ããã«ãããç¥ããæ å ±ã ããã¹ã¯ã¬ã¤ãã³ã°ãã¦JSONã«å¤æãã¦è¿ãã
Cloudflareã®Workersã«TypeScriptã§ã³ã¼ããä½æãã¦ãããã¤ãã¦ããã ãã®TypeScriptã®ã³ã¼ãã§å©ç¨ãã¦ããFWã¯Honoã使ã£ã¦ãããCloudflareä¸ã§åãã«ã¼ãã£ã³ã°ãã®ä»éããããã ä»åã®æ©è½ã®ãµã¤ãºã§ã¯éããå®æããã»ã©ãã®ã§ã¯ãªããFWèªä½ã®ãµã¤ãºãå°ããã®ã§èµ·åæã«æéãããããã¨ããªãã ããã
Workersã®TypeScriptã®ã³ã¼ãã§ãã¼ã¿ããã£ãã·ã¥ããæ¹æ³ã¯ä¸è¨ã®3ã¤ã®ãµã¼ãã¹ã§ã§ãããã ã
Cacheã¯å°åã®ãã¼ã¿ã»ã³ã¿ã¼ã§ä¿åãããããããããã®ãã¼ã¿ã»ã³ã¿ã¼ã¨CDNã®ã¨ãã¸ãµã¼ãã§ä¿åãããã®ã ãããï¼
KVã®æ¹ãæ©ããããããªãã
D1ã¯SQLiteãCDNã¨ãã¸ãµã¼ãã§åãããããã¨ã¦ãæ©ããã ã
ä¸ä½ã©ããä¸çªæ©ãã®ã ãããï¼ããããªãã
ããã§ã¯è¨æ¸¬ã ï¼ï¼ï¼
ãã¼ã«ã«ç°å¢ã§ãã³ããã¼ã¯ãã¹ã
Workersã¯ãã¼ã«ã«ã§éçºã§ããã®ã§ããã³ããã¼ã¯ç¨ã®ã³ã¼ããããããç¨æãã
Cacheçã®è¨æ¸¬ç¨ã³ã¼ã(ã¯ãªãã¯ããã¨å±é)ã
import { Context } from "hono"; export const benchmarkCache = async (c: Context) => { const cache = caches.default; const writeStart = performance.now(); for (let i = 0; i < 100; i++) { const key = `https://example.com/benchmark_key_${i}`; const value = new Response('benchmark_value'); await cache.put(new Request(key), value); } const writeEnd = performance.now(); const readStart = performance.now(); for (let i = 0; i < 100; i++) { const key = `https://example.com/benchmark_key_${i}`; const response = await cache.match(new Request(key)); const value = response ? await response.text() : null; } const readEnd = performance.now(); const totalWriteDuration = writeEnd - writeStart; const totalReadDuration = readEnd - readStart; const avgWriteDuration = totalWriteDuration / 100; const avgReadDuration = totalReadDuration / 100; return c.json({ totalWriteDuration, totalReadDuration, avgWriteDuration, avgReadDuration }); };
KVçã®è¨æ¸¬ç¨ã³ã¼ã(ã¯ãªãã¯ããã¨å±é)ã
import { Context } from "hono";
export const benchmarkKV = async (c: Context) => {
const writeStart = performance.now();
for (let i = 0; i < 100; i++) {
await c.env.kv.put(benchmark_key_${i}
, 'benchmark_value');
}
const writeEnd = performance.now();
const readStart = performance.now();
for (let i = 0; i < 100; i++) {
const value = await c.env.kv.get(`benchmark_key_${i}`);
}
const readEnd = performance.now();
const totalWriteDuration = writeEnd - writeStart;
const totalReadDuration = readEnd - readStart;
const avgWriteDuration = totalWriteDuration / 100;
const avgReadDuration = totalReadDuration / 100;
return c.json({ totalWriteDuration, totalReadDuration, avgWriteDuration, avgReadDuration });
};
D1çã®è¨æ¸¬ç¨ã³ã¼ã(ã¯ãªãã¯ããã¨å±é)ã
import { Context } from "hono";
export const benchmarkD1 = async (c: Context) => {
// ãã¼ãã«ãåå¨ããªãå ´åã¯ä½æ
await c.env.d1.prepare(
CREATE TABLE IF NOT EXISTS benchmark_table (
key TEXT PRIMARY KEY,
value TEXT
)
).run();
// ãã¼ãã«ãã¯ãªã¢
await c.env.d1.prepare(`DELETE FROM benchmark_table`).run();
const writeStart = performance.now();
for (let i = 0; i < 100; i++) {
await c.env.d1.prepare(`INSERT INTO benchmark_table (key, value) VALUES (?, ?)`)
.bind(`benchmark_key_${i}`, 'benchmark_value')
.run();
}
const writeEnd = performance.now();
const readStart = performance.now();
for (let i = 0; i < 100; i++) {
const result = await c.env.d1.prepare(`SELECT value FROM benchmark_table WHERE key = ?`)
.bind(`benchmark_key_${i}`)
.first();
}
const readEnd = performance.now();
const totalWriteDuration = writeEnd - writeStart;
const totalReadDuration = readEnd - readStart;
const avgWriteDuration = totalWriteDuration / 100;
const avgReadDuration = totalReadDuration / 100;
return c.json({ totalWriteDuration, totalReadDuration, avgWriteDuration, avgReadDuration });
};
ä¸è¨ã®ãã³ããã¼ã¯ç¨ã®Workersã®ã³ã¼ãããã¼ã«ã«ã§åããã¨æ¸ãè¾¼ã¿ã¯D1ãä¸çªæ©ããèªã¿è¾¼ã¿ã¯KVãä¸çªæ©ãã
ãã£ãã·ã¥æ¹å¼ | åè¨æ¸ãè¾¼ã¿æé (ms) | åè¨èªã¿è¾¼ã¿æé (ms) | å¹³åæ¸ãè¾¼ã¿æé (ms) | å¹³åèªã¿è¾¼ã¿æé (ms) |
---|---|---|---|---|
Cache | 95 | 35 | 0.95 | 0.35 |
KV | 102 | 17 | 1.02 | 0.17 |
D1 | 71 | 43 | 0.71 | 0.43 |
ä»åã®ä½¿ãæ¹ã ã¨ãã£ãã·ã¥ããã«æ©ãåãåºãã¦è¿ãããéè¦ãªã®ã§ãèªã¿è¾¼ã¿é度ãéè¦ãã¦KVãå©ç¨ããã®ãè¯ãããã ã
Cloudflare Workersä¸ã§é度ãè¨æ¸¬
ä»åº¦ã¯ãå®éã«Cloudflareã®ç°å¢ã§ããããã®æ¹å¼ã使ã£ã¦APIãå®è£ ããã
ãã®ã¢ã¡ãããã¹ã¯ã¬ã¤ãã³ã°ãããã¼ã¿ããã£ãã·ã¥ãããã®ãåãåºãã¦è¿ãAPIã®é度ãæ¯è¼ããã¨ä¸è¨ã®ãããªçµæã¨ãªãããã³ããã¼ã¯ã§è¨æ¸¬ããã ãã§ã¯ããããªãã£ãäºãå¤æããã
ãã£ãã·ã¥æ¹å¼ | å¹³åé度 | åè |
---|---|---|
ãã£ãã·ã¥ãªã | 200ms | |
Cache | 25ms~50ms | 80-100msã¨ä¸æ¯ããããã¨ãå¤ã |
KV | 30msç¨åº¦ | å°ãéãããã¦åããã¨ãã«ã¢ã¯ã»ã¹ããéã«Lambdaçãªã®ãèµ·åããã®ãæåã ã500msãããããã |
D1 | 480msç¨åº¦ | ãã£ãã·ã¥ãªãããé ããKVã¨åæ§ã«èµ·åæã¯1ç§ä»¥ä¸ããã |
ãã®çµæãããAPIä¸ã®ãã£ãã·ã¥ã¯æåã®èµ·åãé ããå®å®çãªKVãå©ç¨ãããã¨ã¨ããã D1ãæ©ããã¨ãæå¾ ãããå°ãªãã¨ã2024å¹´ç¾å¨ã§ã¯ãä»åã®ä½¿ãæ¹ã§ã¯KVã«è»é ãä¸ãã£ãã
ã©ãã«ã§ããã£ãã·ã¥ãä»æããï¼
ãã£ãã·ã¥ã¯ã§ããã ãè¿ãã¨ããã«ä»æããã®ãå¹æãé«ã
ã§ãã£ãã·ã¥ããã»ããå¹æã¦ãããã ã
ããããã¾ã§ã«Cloudflareã®å©ç¨ã«ãã£ã¦ãHTMLãç»åãªã©éçãªãã¡ã¤ã«ã¯CDNã§ãã£ãã·ã¥ããCache-Controlãã¬ã¹ãã³ã¹ãããã¼ã«ã¤ãããã¨ã«ãã£ã¦ãã©ã¦ã¶ã«ãã£ãã·ã¥ãå©ç¨ãã¦ããã
WebAPIã§ã¯Workersã«ããã¦ã¢ã¡ãããã¹ã¯ã¬ã¤ãã³ã°ãããã¼ã¿ãKVã«ä¿åãããã¨ã§ãã£ãã·ã¥ããããã«ããã
ã¾ã ã ãã¾ã ã¾ã è¡ããã
ï¼ï¼
WebAPIã®ã¢ã¯ã»ã¹ãã¯ã©ã¤ã¢ã³ãã§ãã£ãã·ã¥ããããµã¼ãã«ã¢ã¯ã»ã¹ããã«æ¸ããããããªããï¼
ãããªæãã§useEffectã«ä»è¾¼ãã°WebAPIã¢ã¯ã»ã¹ããã£ãã·ã¥å©ç¨ãã¦ãã£ã³ã»ã«ã§ãã
const cachedArticles = localStorage.getItem('latestArticles'); const cachedTime = localStorage.getItem('cacheTime'); const now = new Date().getTime(); if (cachedArticles && cachedTime && now - cachedTime < 600000) { setLatestArticles(JSON.parse(cachedArticles)); setLoading(false); return; }
æçµççµæ
æçµçãªããã©ã¼ãã³ã¹ãè¦ã¦è¦ãã
HAâYAâSUâGIâRU
ããåéç¨ã§å·¥å¤«ããç®æãããä¸åº¦ã¾ã¨ãã¦ã¿ãã
é ç® | å 容 |
---|---|
SSG FW | Astro |
ä»ãã¼ã¸ã®å èªã¿ | Prefetch |
ãããã³ã« | HTTP/3 |
CDNãµã¼ã | æ±äº¬ãªã¼ã¸ã§ã³ |
WebAPIã®å®è¡ | Cloudflareã®ã¨ãã¸ãµã¼ã |
WebAPIã®FW | Hono |
WebAPIã®ãã£ãã·ã¥ | KVã®å©ç¨ |
ãã©ã¦ã¶éçãã¡ã¤ã«ã®ãã£ãã·ã¥ | Cache-Controlã®å©ç¨ |
ãã©ã¦ã¶WebAPIã¢ã¯ã»ã¹ã®ãã£ãã·ã¥ | LocalStorageã®å©ç¨ |
ã¾ã¨ã
ãããã ã£ãã ãããï¼è¦ãç®çã«ã¯åãç«ã¦ã¦æ´¾æãã®ãªãHPã§ãè¦ç·ãå¤ããã¨éã¹ãä½ç½ããããã¨ã証æã§ããã®ã§ã¯ãªãã ãããï¼
é«éåã®éç¨ãè¦ãããã¨ããåãã"ã¹ãã¼ãã®åããå´"ã«å°ããã¨ãã§ããã®ã§ã¯ãªãã ãããã
ã¡ãªã¿ã«ãªã¬ã¯ãã¬ã¸ã³ããã¯ã¸ã£ã³ãæ´¾ãªã®ã§ç¹æ»ã®æãé£è¼ãã¦ããé ã®æ¼«ç»ã ã¨å¹½éç½æ¸ããããã«å£å¿ãå¹å¼µã好ãã§ãã