SEO
SEO 最重要的方面是创建高质量的内容,使其能在网络上被广泛链接。但是,构建能够良好排名的网站,还需要考虑一些技术因素。
开箱即用的功能
SSR(服务端渲染)
虽然近年来搜索引擎在索引客户端 JavaScript 渲染的内容方面有所改善,但服务端渲染的内容仍然能够更频繁和可靠地被索引。SvelteKit 默认采用 SSR,虽然您可以在handle
中禁用它,但除非有充分的理由,否则应该保持启用。
SvelteKit 的渲染是高度可配置的,您可以根据需要实现动态渲染。但这通常不推荐,因为 SSR 除了 SEO 之外还有其他好处。
性能
核心网页指标等信号会影响搜索引擎排名。由于 Svelte 和 SvelteKit 引入的开销最小,因此更容易构建高性能网站。您可以使用 Google 的 PageSpeed Insights 或 Lighthouse 测试您的网站性能。详情请阅读性能页面。
URL 标准化
SvelteKit 会将带有尾部斜杠的路径重定向到不带斜杠的路径(或根据您的配置反向操作),因为重复的 URL 对 SEO 不利。
手动设置
<title> 和 <meta>
每个页面都应该在 <svelte:head>
中包含精心编写的独特的 <title>
和 <meta name="description">
元素。关于如何编写描述性标题和描述,以及其他使内容便于搜索引擎理解的建议,可以在Google的 Lighthouse SEO 审核 文档中找到。
一个常见的模式是从页面的
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) => Response
This Fetch API interface represents the response to a request.
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 | undefined
headers: {
'Content-Type': 'application/xml'
}
}
);
}
AMP
现代网络开发的一个不幸现实是有时需要创建网站的加速移动页面(AMP)版本。在 SvelteKit 中,这可以通过设置 inlineStyleThreshold
选项来实现...
/** @type {import('@sveltejs/kit').Config} */
const const config: {
kit: {
inlineStyleThreshold: number;
};
}
config = {
kit: {
inlineStyleThreshold: number;
}
kit: {
// 由于不允许使用<link rel="stylesheet">,
// 内联所有样式
inlineStyleThreshold: number
inlineStyleThreshold: var Infinity: number
Infinity
}
};
export default const config: {
kit: {
inlineStyleThreshold: number;
};
}
config;
...在根 +layout.js
/ +layout.server.js
中禁用 csr
...
export const const csr: false
csr = false;
...在app.html
中添加amp
...
<html amp>
...
</html>
...并使用从 @sveltejs/amp
导入的 transform
和 transformPageChunk
转换 HTML:
import * as import amp
amp from '@sveltejs/amp';
/** @type {import('@sveltejs/kit').Handle} */
export async function function handle({ event, resolve }: {
event: any;
resolve: any;
}): Promise<any>
handle({ event: any
event, resolve: any
resolve }) {
let let buffer: string
buffer = '';
return await resolve: any
resolve(event: any
event, {
transformPageChunk: ({ html, done }: {
html: any;
done: any;
}) => string | undefined
transformPageChunk: ({ html: any
html, done: any
done }) => {
let buffer: string
buffer += html: any
html;
if (done: any
done) return import amp
amp.function transform(html: string): string
transform(let buffer: string
buffer);
}
});
}
import * as import amp
amp 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: Handle
handle: 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: string
buffer = '';
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.
transformPageChunk: ({ html: string
html, done: boolean
done }) => {
let buffer: string
buffer += html: string
html;
if (done: boolean
done) return import amp
amp.function transform(html: string): string
transform(let buffer: string
buffer);
}
});
};
为了防止在将页面转换为 amp 时发送任何未使用的 CSS,我们可以使用dropcss
:
import * as import amp
amp 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<...>
handle({ event: RequestEvent<Partial<Record<string, string>>, string | null>
event, resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>
resolve }) {
let let buffer: string
buffer = '';
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.
transformPageChunk: ({ html: string
html, done: boolean
done }) => {
let buffer: string
buffer += html: string
html;
if (done: boolean
done) {
let let css: string
css = '';
const const markup: string
markup = import amp
amp
.function transform(html: string): string
transform(let buffer: string
buffer)
.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无法处理此字符
.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: string
match, attributes: any
attributes, contents: any
contents) => {
let css: string
css = contents: any
contents;
return `<style amp-custom${attributes: any
attributes}></style>`;
});
let css: string
css = module "dropcss"
dropcss({ css: string
css, html: string
html: const markup: string
markup }).css;
return const markup: string
markup.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: string
css}</style>`);
}
}
});
}
import * as import amp
amp 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: Handle
handle: 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: string
buffer = '';
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.
transformPageChunk: ({ html: string
html, done: boolean
done }) => {
let buffer: string
buffer += html: string
html;
if (done: boolean
done) {
let let css: string
css = '';
const const markup: string
markup = import amp
amp
.function transform(html: string): string
transform(let buffer: string
buffer)
.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无法处理此字符
.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: string
match, attributes: any
attributes, contents: any
contents) => {
let css: string
css = contents: any
contents;
return `<style amp-custom${attributes: any
attributes}></style>`;
});
let css: string
css = module "dropcss"
dropcss({ css: string
css, html: string
html: const markup: string
markup }).css;
return const markup: string
markup.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: string
css}</style>`);
}
}
});
};
使用
handle
hook 通过amphtml-validator
验证转换后的 HTML 是个好主意,但由于速度很慢,仅建议在预渲染页面时使用。