Skip to main content

Best practices

SEO

Edit this page on GitHub

SEO で最も重要なのは、高品質なコンテンツを作ること、そしてそれが web 上で広くリンクされることです。しかし、ランクが高いサイトを構築するためにいくつか技術的に考慮すべきこともあります。

Out of the box

SSR

近年、検索エンジンはクライアントサイドの JavaScript でレンダリングされたコンテンツのインデックスを改善してきましたが、サーバーサイドレンダリングされたコンテンツのほうがより頻繁に、より確実にインデックスされます。SvelteKit はデフォルトで SSR を採用しています。handle で無効にすることもできますが、適切な理由がない場合はそのままにしておきましょう。

SvelteKit のレンダリングは高度な設定が可能です。必要であれば、動的なレンダリング(dynamic rendering) を実装することも可能です。一般的には推奨されません、SSR には SEO 以外のメリットもあるからです。

パフォーマンス

Core Web Vitals のような指標は検索エンジンのランクに影響を与えます。Svelte と SvelteKit はオーバーヘッドが最小限であるため、ハイパフォーマンスなサイトを簡単に構築できです。Google の PageSpeed InsightsLighthouse で、ご自身のサイトをテストすることができます。詳細は パフォーマンスのページ をお読みください。

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 として) 使用することです。

サイトマップ

サイトマップ は、検索エンジンがサイト内のページの優先順位付けをするのに役立ちます、特にコンテンツの量が多い場合は。エンドポイントを使用してサイトマップを動的に作成できます:

src/routes/sitemap.xml/+server.js
ts
export async function GET() {
return new Response(
`
<?xml version="1.0" encoding="UTF-8" ?>
<urlset
xmlns="https://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="https://www.w3.org/1999/xhtml"
xmlns:mobile="https://www.google.com/schemas/sitemap-mobile/1.0"
xmlns:news="https://www.google.com/schemas/sitemap-news/0.9"
xmlns:image="https://www.google.com/schemas/sitemap-image/1.1"
xmlns:video="https://www.google.com/schemas/sitemap-video/1.1"
>
<!-- <url> elements go here -->
</urlset>`.trim(),
{
headers: {
'Content-Type': 'application/xml'
}
}
);
}
src/routes/sitemap.xml/+server.ts
ts
export async function GET() {
return new Response(
`
<?xml version="1.0" encoding="UTF-8" ?>
<urlset
xmlns="https://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="https://www.w3.org/1999/xhtml"
xmlns:mobile="https://www.google.com/schemas/sitemap-mobile/1.0"
xmlns:news="https://www.google.com/schemas/sitemap-news/0.9"
xmlns:image="https://www.google.com/schemas/sitemap-image/1.1"
xmlns:video="https://www.google.com/schemas/sitemap-video/1.1"
>
<!-- <url> elements go here -->
</urlset>`.trim(),
{
headers: {
'Content-Type': 'application/xml',
},
},
);
}

AMP

現代の web 開発における不幸な現実として、サイトの Accelerated Mobile Pages (AMP) バージョンを作らなければならないときがある、というのがあります。SvelteKit では、inlineStyleThreshold オプションを設定することでこれを実現することができます…

svelte.config.js
ts
/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
// since <link rel="stylesheet"> isn't
// allowed, inline all styles
inlineStyleThreshold: Infinity
}
};
export default config;

…最上位(root)の +layout.js/+layout.server.jscsr を無効にします…

src/routes/+layout.server.js
ts
export const csr = false;
src/routes/+layout.server.ts
ts
export const csr = false;

ampapp.html に追加します

<html amp>
...

…そして、transformPageChunk と、@sveltejs/amp からインポートできる transform を使用して、HTML を変換します:

src/hooks.server.js
ts
import * as amp from '@sveltejs/amp';
/** @type {import('@sveltejs/kit').Handle} */
export async function handle({ event, resolve }) {
let buffer = '';
return await resolve(event, {
transformPageChunk: ({ html, done }) => {
buffer += html;
if (done) return amp.transform(buffer);
}
});
}
src/hooks.server.ts
ts
import * as amp from '@sveltejs/amp';
import type { Handle } from '@sveltejs/kit';
export const handle: Handle = async ({ event, resolve }) => {
let buffer = '';
return await resolve(event, {
transformPageChunk: ({ html, done }) => {
buffer += html;
if (done) return amp.transform(buffer);
},
});
};

ページを amp に変換した結果として未使用の CSS が配布されてしまうのを防ぎたければ、dropcss を使用すると良いでしょう:

src/hooks.server.js
ts
import * as amp from '@sveltejs/amp';
import dropcss from 'dropcss';
Cannot find module 'dropcss' or its corresponding type declarations.2307Cannot find module 'dropcss' or its corresponding type declarations.
/** @type {import('@sveltejs/kit').Handle} */
export async function handle({ event, resolve }) {
let buffer = '';
return await resolve(event, {
transformPageChunk: ({ html, done }) => {
buffer += html;
if (done) {
let css = '';
const markup = amp
.transform(buffer)
.replace('⚡', 'amp') // dropcss can't handle this character
.replace(/<style amp-custom([^>]*?)>([^]+?)<\/style>/, (match, attributes, contents) => {
css = contents;
return `<style amp-custom${attributes}></style>`;
});
css = dropcss({ css, html: markup }).css;
return markup.replace('</style>', `${css}</style>`);
}
}
});
}
src/hooks.server.ts
ts
import * as amp from '@sveltejs/amp';
import dropcss from 'dropcss';
Cannot find module 'dropcss' or its corresponding type declarations.2307Cannot find module 'dropcss' or its corresponding type declarations.
import type { Handle } from '@sveltejs/kit';
export const handle: Handle = async ({ event, resolve }) => {
let buffer = '';
return await resolve(event, {
transformPageChunk: ({ html, done }) => {
buffer += html;
if (done) {
let css = '';
const markup = amp
.transform(buffer)
.replace('⚡', 'amp') // dropcss can't handle this character
.replace(/<style amp-custom([^>]*?)>([^]+?)<\/style>/, (match, attributes, contents) => {
css = contents;
return `<style amp-custom${attributes}></style>`;
});
css = dropcss({ css, html: markup }).css;
return markup.replace('</style>', `${css}</style>`);
}
},
});
};

amphtml-validator を使用して変換された HTML を検証するのに、handle hook を利用するのは良いアイデアですが、非常に遅くなってしまうので、ページをプリレンダリングするときだけにしてください。