SEO
SEO で最も重要なのは、高品質なコンテンツを作ること、そしてそれが web 上で広くリンクされることです。しかし、ランクが高いサイトを構築するためにいくつか技術的に考慮すべきこともあります。
Out of the box
SSR
近年、検索エンジンはクライアントサイドの JavaScript でレンダリングされたコンテンツのインデックスを改善してきましたが、サーバーサイドレンダリングされたコンテンツのほうがより頻繁に、より確実にインデックスされます。SvelteKit はデフォルトで SSR を採用しています。handle で無効にすることもできますが、適切な理由がない場合はそのままにしておきましょう。
SvelteKit のレンダリングは高度な設定が可能です。必要であれば、動的なレンダリング(dynamic rendering) を実装することも可能です。一般的には推奨されません、SSR には SEO 以外のメリットもあるからです。
パフォーマンス
Core Web Vitals のような指標は検索エンジンのランクに影響を与えます。Svelte と SvelteKit はオーバーヘッドが最小限であるため、ハイパフォーマンスなサイトを簡単に構築できです。Google の PageSpeed Insights や Lighthouse で、ご自身のサイトをテストすることができます。SvelteKit のデフォルトの ハイブリッドレンダリングモードや画像の最適化といったいくつかの重要な処置を講じることで、サイトの速度を大幅に改善することができます。詳細は パフォーマンスのページ をお読みください。
URLの正規化
SvelteKit は、末尾のスラッシュ(trailing slash)付きのパス名から、末尾のスラッシュが無いパス名にリダイレクトします (設定 で逆にできます)。URLの重複は、SEOに悪影響を与えます。
Manual setup
<title> と <meta>
全てのページで、よく練られたユニークな <title> と <meta name="description"> を <svelte:head> の内側に置くべきです。説明的な title と description の書き方に関するガイダンスと、検索エンジンにとってわかりやすいコンテンツを作るためのその他の方法については、Google の Lighthouse SEO audits のドキュメントで見つけることができます。
よくあるパターンとしては、ページの
load関数から SEO 関連のdataを返し、それを最上位のレイアウトの<svelte:head>で (page.dataとして) 使用することです。
サイトマップ
サイトマップ は、検索エンジンがサイト内のページの優先順位付けをするのに役立ちます、特にコンテンツの量が多い場合は。エンドポイントを使用してサイトマップを動的に作成できます:
export async function function GET(): Promise<Response>GET() {
return new var Response: new (body?: BodyInit | null, init?: ResponseInit) => ResponseThe Response interface of the Fetch API represents the response to a request.
Response(
`
<?xml version="1.0" encoding="UTF-8" ?>
<urlset
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml"
xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0"
xmlns:news="http://www.google.com/schemas/sitemap-news/0.9"
xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"
xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"
>
<!-- <url> elements go here -->
</urlset>`.String.trim(): stringRemoves the leading and trailing white space and line terminator characters from a string.
trim(),
{
ResponseInit.headers?: HeadersInit | undefinedheaders: {
'Content-Type': 'application/xml'
}
}
);
}AMP
現代の web 開発における不幸な現実として、サイトの Accelerated Mobile Pages (AMP) バージョンを作らなければならないときがある、というのがあります。SvelteKit では、inlineStyleThreshold オプションを設定することでこれを実現することができます…
/** @type {import('@sveltejs/kit').Config} */
const const config: Configconfig = {
Config.kit?: KitConfig | undefinedSvelteKit options.
kit: {
// since <link rel="stylesheet"> isn't
// allowed, inline all styles
KitConfig.inlineStyleThreshold?: number | undefinedInline CSS inside a <style> block at the head of the HTML. This option is a number that specifies the maximum length of a CSS file in UTF-16 code units, as specified by the String.length property, to be inlined. All CSS files needed for the page that are smaller than this value are merged and inlined in a <style> block.
This results in fewer initial requests and can improve your First Contentful Paint score. However, it generates larger HTML output and reduces the effectiveness of browser caches. Use it advisedly.
inlineStyleThreshold: var Infinity: numberInfinity
}
};
export default const config: Configconfig;…最上位(root)の +layout.js / +layout.server.js の csr を無効にします…
export const const csr: falsecsr = false;…amp を app.html に追加します
<html amp>
...…そして、transformPageChunk と、@sveltejs/amp からインポートできる transform を使用して、HTML を変換します:
import * as import ampamp from '@sveltejs/amp';
/** @type {import('@sveltejs/kit').Handle} */
export async function function handle(input: {
event: RequestEvent;
resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>;
}): MaybePromise<Response>
handle({ event: RequestEvent<Record<string, string>, string | null>event, resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>resolve }) {
let let buffer: stringbuffer = '';
return await resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>resolve(event: RequestEvent<Record<string, string>, string | null>event, {
ResolveOptions.transformPageChunk?: ((input: {
html: string;
done: boolean;
}) => MaybePromise<string | undefined>) | undefined
Applies custom transforms to HTML. If done is true, it's the final chunk. Chunks are not guaranteed to be well-formed HTML
(they could include an element's opening tag but not its closing tag, for example)
but they will always be split at sensible boundaries such as %sveltekit.head% or layout/page components.
transformPageChunk: ({ html: stringhtml, done: booleandone }) => {
let buffer: stringbuffer += html: stringhtml;
if (done: booleandone) return import ampamp.function transform(html: string): stringtransform(let buffer: stringbuffer);
}
});
}import * as import ampamp from '@sveltejs/amp';
import type { type Handle = (input: {
event: RequestEvent;
resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>;
}) => MaybePromise<Response>
The handle hook runs every time the SvelteKit server receives a request and
determines the response.
It receives an event object representing the request and a function called resolve, which renders the route and generates a Response.
This allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing routes programmatically, for example).
Handle } from '@sveltejs/kit';
export const const handle: Handlehandle: type Handle = (input: {
event: RequestEvent;
resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>;
}) => MaybePromise<Response>
The handle hook runs every time the SvelteKit server receives a request and
determines the response.
It receives an event object representing the request and a function called resolve, which renders the route and generates a Response.
This allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing routes programmatically, for example).
Handle = async ({ event: RequestEvent<Record<string, string>, string | null>event, resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>resolve }) => {
let let buffer: stringbuffer = '';
return await resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>resolve(event: RequestEvent<Record<string, string>, string | null>event, {
ResolveOptions.transformPageChunk?: ((input: {
html: string;
done: boolean;
}) => MaybePromise<string | undefined>) | undefined
Applies custom transforms to HTML. If done is true, it's the final chunk. Chunks are not guaranteed to be well-formed HTML
(they could include an element's opening tag but not its closing tag, for example)
but they will always be split at sensible boundaries such as %sveltekit.head% or layout/page components.
transformPageChunk: ({ html: stringhtml, done: booleandone }) => {
let buffer: stringbuffer += html: stringhtml;
if (done: booleandone) return import ampamp.function transform(html: string): stringtransform(let buffer: stringbuffer);
}
});
};ページを amp に変換した結果として未使用の CSS が配布されてしまうのを防ぎたければ、dropcss を使用すると良いでしょう:
import * as import ampamp from '@sveltejs/amp';
import module "dropcss"dropcss from 'dropcss';
/** @type {import('@sveltejs/kit').Handle} */
export async function function handle(input: {
event: RequestEvent;
resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>;
}): MaybePromise<Response>
handle({ event: RequestEvent<Record<string, string>, string | null>event, resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>resolve }) {
let let buffer: stringbuffer = '';
return await resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>resolve(event: RequestEvent<Record<string, string>, string | null>event, {
ResolveOptions.transformPageChunk?: ((input: {
html: string;
done: boolean;
}) => MaybePromise<string | undefined>) | undefined
Applies custom transforms to HTML. If done is true, it's the final chunk. Chunks are not guaranteed to be well-formed HTML
(they could include an element's opening tag but not its closing tag, for example)
but they will always be split at sensible boundaries such as %sveltekit.head% or layout/page components.
transformPageChunk: ({ html: stringhtml, done: booleandone }) => {
let buffer: stringbuffer += html: stringhtml;
if (done: booleandone) {
let let css: stringcss = '';
const const markup: stringmarkup = import ampamp
.function transform(html: string): stringtransform(let buffer: stringbuffer)
.String.replace(searchValue: string | RegExp, replaceValue: string): string (+3 overloads)Replaces text in a string, using a regular expression or search string.
replace('⚡', 'amp') // dropcss can't handle this character
.String.replace(searchValue: {
[Symbol.replace](string: string, replacer: (substring: string, ...args: any[]) => string): string;
}, replacer: (substring: string, ...args: any[]) => string): string (+3 overloads)
Replaces text in a string, using an object that supports replacement within a string.
replace(/<style amp-custom([^>]*?)>([^]+?)<\/style>/, (match: stringmatch, attributes: anyattributes, contents: anycontents) => {
let css: stringcss = contents: anycontents;
return `<style amp-custom${attributes: anyattributes}></style>`;
});
let css: stringcss = module "dropcss"dropcss({ css: stringcss, html: stringhtml: const markup: stringmarkup }).css;
return const markup: stringmarkup.String.replace(searchValue: string | RegExp, replaceValue: string): string (+3 overloads)Replaces text in a string, using a regular expression or search string.
replace('</style>', `${let css: stringcss}</style>`);
}
}
});
}
import * as import ampamp from '@sveltejs/amp';
import module "dropcss"dropcss from 'dropcss';
import type { type Handle = (input: {
event: RequestEvent;
resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>;
}) => MaybePromise<Response>
The handle hook runs every time the SvelteKit server receives a request and
determines the response.
It receives an event object representing the request and a function called resolve, which renders the route and generates a Response.
This allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing routes programmatically, for example).
Handle } from '@sveltejs/kit';
export const const handle: Handlehandle: type Handle = (input: {
event: RequestEvent;
resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>;
}) => MaybePromise<Response>
The handle hook runs every time the SvelteKit server receives a request and
determines the response.
It receives an event object representing the request and a function called resolve, which renders the route and generates a Response.
This allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing routes programmatically, for example).
Handle = async ({ event: RequestEvent<Record<string, string>, string | null>event, resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>resolve }) => {
let let buffer: stringbuffer = '';
return await resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>resolve(event: RequestEvent<Record<string, string>, string | null>event, {
ResolveOptions.transformPageChunk?: ((input: {
html: string;
done: boolean;
}) => MaybePromise<string | undefined>) | undefined
Applies custom transforms to HTML. If done is true, it's the final chunk. Chunks are not guaranteed to be well-formed HTML
(they could include an element's opening tag but not its closing tag, for example)
but they will always be split at sensible boundaries such as %sveltekit.head% or layout/page components.
transformPageChunk: ({ html: stringhtml, done: booleandone }) => {
let buffer: stringbuffer += html: stringhtml;
if (done: booleandone) {
let let css: stringcss = '';
const const markup: stringmarkup = import ampamp
.function transform(html: string): stringtransform(let buffer: stringbuffer)
.String.replace(searchValue: string | RegExp, replaceValue: string): string (+3 overloads)Replaces text in a string, using a regular expression or search string.
replace('⚡', 'amp') // dropcss can't handle this character
.String.replace(searchValue: {
[Symbol.replace](string: string, replacer: (substring: string, ...args: any[]) => string): string;
}, replacer: (substring: string, ...args: any[]) => string): string (+3 overloads)
Replaces text in a string, using an object that supports replacement within a string.
replace(/<style amp-custom([^>]*?)>([^]+?)<\/style>/, (match: stringmatch, attributes: anyattributes, contents: anycontents) => {
let css: stringcss = contents: anycontents;
return `<style amp-custom${attributes: anyattributes}></style>`;
});
let css: stringcss = module "dropcss"dropcss({ css: stringcss, html: stringhtml: const markup: stringmarkup }).css;
return const markup: stringmarkup.String.replace(searchValue: string | RegExp, replaceValue: string): string (+3 overloads)Replaces text in a string, using a regular expression or search string.
replace('</style>', `${let css: stringcss}</style>`);
}
}
});
};
amphtml-validatorを使用して変換された HTML を検証するのに、handlehook を利用するのは良いアイデアですが、非常に遅くなってしまうので、ページをプリレンダリングするときだけにしてください。
Edit this page on GitHub llms.txt