Skip to main content

SEO

SEO 最重要的方面是创建高质量的内容,使其能在网络上被广泛链接。但是,构建能够良好排名的网站,还需要考虑一些技术因素。

开箱即用的功能

SSR(服务端渲染)

虽然近年来搜索引擎在索引客户端 JavaScript 渲染的内容方面有所改善,但服务端渲染的内容仍然能够更频繁和可靠地被索引。SvelteKit 默认采用 SSR,虽然您可以在handle中禁用它,但除非有充分的理由,否则应该保持启用。

SvelteKit 的渲染是高度可配置的,您可以根据需要实现动态渲染。但这通常不推荐,因为 SSR 除了 SEO 之外还有其他好处。

性能

核心网页指标等信号会影响搜索引擎排名。由于 Svelte 和 SvelteKit 引入的开销最小,因此更容易构建高性能网站。您可以使用 Google 的 PageSpeed InsightsLighthouse 测试您的网站性能。详情请阅读性能页面

URL 标准化

SvelteKit 会将带有尾部斜杠的路径重定向到不带斜杠的路径(或根据您的配置反向操作),因为重复的 URL 对 SEO 不利。

手动设置

<title> 和 <meta>

每个页面都应该在 <svelte:head> 中包含精心编写的独特的 <title><meta name="description"> 元素。关于如何编写描述性标题和描述,以及其他使内容便于搜索引擎理解的建议,可以在Google的 Lighthouse SEO 审核 文档中找到。

一个常见的模式是从页面的 load 函数返回与 SEO 相关的 data,然后在根布局<svelte:head> 中使用它(作为page.data)。

站点地图

站点地图可帮助搜索引擎对网站内的页面进行优先级排序,特别是当您有大量内容时。您可以使用端点动态创建站点地图:

src/routes/sitemap.xml/+server
export async function function GET(): Promise<Response>GET() {
	return new var Response: new (body?: BodyInit | null, init?: ResponseInit) => Response

This Fetch API interface represents the response to a request.

MDN Reference

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>元素放在这里 --> </urlset>`.String.trim(): string

Removes the leading and trailing white space and line terminator characters from a string.

trim
(),
{ ResponseInit.headers?: HeadersInit | undefinedheaders: { 'Content-Type': 'application/xml' } } ); }

AMP

现代网络开发的一个不幸现实是有时需要创建网站的加速移动页面(AMP)版本。在 SvelteKit 中,这可以通过设置 inlineStyleThreshold 选项来实现...

svelte.config
/** @type {import('@sveltejs/kit').Config} */
const 
const config: {
    kit: {
        inlineStyleThreshold: number;
    };
}
@type{import('@sveltejs/kit').Config}
config
= {
kit: {
    inlineStyleThreshold: number;
}
kit
: {
// 由于不允许使用<link rel="stylesheet">, // 内联所有样式 inlineStyleThreshold: numberinlineStyleThreshold: var Infinity: numberInfinity } }; export default
const config: {
    kit: {
        inlineStyleThreshold: number;
    };
}
@type{import('@sveltejs/kit').Config}
config
;

...在根 +layout.js / +layout.server.js 中禁用 csr...

src/routes/+layout.server
export const const csr: falsecsr = false;

...在app.html中添加amp...

<html amp>
	...
</html>

...并使用从 @sveltejs/amp 导入的 transformtransformPageChunk 转换 HTML:

src/hooks.server
import * as import ampamp from '@sveltejs/amp';

/** @type {import('@sveltejs/kit').Handle} */
export async function 
function handle({ event, resolve }: {
    event: any;
    resolve: any;
}): Promise<any>
@type{import('@sveltejs/kit').Handle}
handle
({ event: anyevent, resolve: anyresolve }) {
let let buffer: stringbuffer = ''; return await resolve: anyresolve(event: anyevent, {
transformPageChunk: ({ html, done }: {
    html: any;
    done: any;
}) => string | undefined
transformPageChunk
: ({ html: anyhtml, done: anydone }) => {
let buffer: stringbuffer += html: anyhtml; if (done: anydone) 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<...>

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<...>

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<Partial<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<Partial<Record<string, string>>, string | null>event, {
ResolveOptions.transformPageChunk?(input: {
    html: string;
    done: boolean;
}): MaybePromise<string | 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.

@paraminput the html chunk and the info if this is the last chunk
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

src/hooks.server
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<...>
@type{import('@sveltejs/kit').Handle}
handle
({ event: RequestEvent<Partial<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<Partial<Record<string, string>>, string | null>event, {
ResolveOptions.transformPageChunk?(input: {
    html: string;
    done: boolean;
}): MaybePromise<string | 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.

@paraminput the html chunk and the info if this is the last chunk
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.

@paramsearchValue A string or regular expression to search for.
@paramreplaceValue A string containing the text to replace. When the {@linkcode searchValue} is a RegExp, all matches are replaced if the g flag is set (or only those matches at the beginning, if the y flag is also present). Otherwise, only the first match of {@linkcode searchValue} is replaced.
replace
('⚡', 'amp') // dropcss无法处理此字符
.
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.

@paramsearchValue A object can search for and replace matches within a string.
@paramreplacer A function that returns the replacement text.
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.

@paramsearchValue A string or regular expression to search for.
@paramreplaceValue A string containing the text to replace. When the {@linkcode searchValue} is a RegExp, all matches are replaced if the g flag is set (or only those matches at the beginning, if the y flag is also present). Otherwise, only the first match of {@linkcode searchValue} is replaced.
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<...>

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<...>

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<Partial<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<Partial<Record<string, string>>, string | null>event, {
ResolveOptions.transformPageChunk?(input: {
    html: string;
    done: boolean;
}): MaybePromise<string | 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.

@paraminput the html chunk and the info if this is the last chunk
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.

@paramsearchValue A string or regular expression to search for.
@paramreplaceValue A string containing the text to replace. When the {@linkcode searchValue} is a RegExp, all matches are replaced if the g flag is set (or only those matches at the beginning, if the y flag is also present). Otherwise, only the first match of {@linkcode searchValue} is replaced.
replace
('⚡', 'amp') // dropcss无法处理此字符
.
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.

@paramsearchValue A object can search for and replace matches within a string.
@paramreplacer A function that returns the replacement text.
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.

@paramsearchValue A string or regular expression to search for.
@paramreplaceValue A string containing the text to replace. When the {@linkcode searchValue} is a RegExp, all matches are replaced if the g flag is set (or only those matches at the beginning, if the y flag is also present). Otherwise, only the first match of {@linkcode searchValue} is replaced.
replace
('</style>', `${let css: stringcss}</style>`);
} } }); };

使用 handle hook 通过 amphtml-validator 验证转换后的 HTML 是个好主意,但由于速度很慢,仅建议在预渲染页面时使用。

在 GitHub 编辑此页面

上一页 下一页