SEO
最重要的 SEO 方面是创建高质量的内容,这些内容在网络上被广泛链接。然而,对于构建排名良好的网站,还有一些技术考虑因素。
即开即用
SSR
尽管近年来搜索引擎在索引使用客户端 JavaScript 渲染的内容方面有所改进,但服务器端渲染的内容索引得更频繁且更可靠。SvelteKit 默认采用 SSR(服务器端渲染),尽管你可以在handle中禁用它,除非你有充分的理由不这样做,否则你应该保持开启状态。
[!注意] SvelteKit 的渲染高度可配置,您如果需要可以实施 动态渲染。通常不推荐这样做,因为 SSR 除了 SEO 之外还有其他好处。
性能
信号如核心网页关键指标影响搜索引擎排名。由于 Svelte 和 SvelteKit 引入的额外开销最小,因此构建高性能网站更容易。您可以使用 Google 的页面速度洞察或灯塔测试您网站的性能。阅读性能页面获取更多详细信息。
标准化 URL
SvelteKit 将带有尾随斜杠的路径名重定向到不带斜杠的路径名(或反之亦然,具体取决于您的配置),因为重复的 URL 对 SEO 不利。
手动设置
<标题> 和 <元数据>
每页都应该包含编写良好且独特的 <title> 和 <meta name="description"> 元素,这些元素位于 <svelte:head> 内。有关如何编写描述性标题和描述的指南,以及如何使内容更容易被搜索引擎理解的其它建议,可以在 Google 的 Lighthouse SEO audits 文档中找到。
[!注意] 常见的模式是从页面
加载函数返回与 SEO 相关的数据,然后将其(作为page.data)用于根 布局 中的<svelte:head>。
网站地图
网站地图帮助搜索引擎优先处理您网站内的页面,尤其是当您有大量内容时。您可以使用端点动态创建网站地图。
export async function function GET(): Promise<Response>GET() {
return new var Response: new (body?: BodyInit | null, init?: ResponseInit) => ResponseThis 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> 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
现代网页开发的一个不幸现实是,有时有必要创建一个加速移动页面(AMP)版本的网站。在 SvelteKit 中,可以通过设置inlineStyleThreshold选项来完成此操作...
/** @type {import('@sveltejs/kit').Config} */
const const config: {
kit: {
inlineStyleThreshold: number;
};
}
config = {
kit: {
inlineStyleThreshold: number;
}
kit: {
// since <link rel="stylesheet"> isn't
// allowed, inline all styles
inlineStyleThreshold: numberinlineStyleThreshold: var Infinity: numberInfinity
}
};
export default const config: {
kit: {
inlineStyleThreshold: number;
};
}
config;...在您的根目录 csr 中禁用 +layout.js / +layout.server.js...
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({ event, resolve }: {
event: any;
resolve: any;
}): Promise<any>
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>) | 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<...>
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>) | 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<...>
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>) | 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>`);
}
}
});
};[!注意] 使用
handle钩子来验证转换后的 HTML 使用amphtml-validator是一个好主意,但仅当你在预渲染页面时,因为它非常慢。
Edit this page on GitHub llms.txt