Sveltekit中理解适配器静态的缺失指南
#javascript #网络开发人员 #pwa #svelte

在本指南中,我们将浏览adapter-static的特性和怪癖,这在所有SvelteKit adapters中都是唯一的,因为它将其编译到HTML,CSS和JavaScript的文件夹中,而不是无服务器函数或Node.js Server。

许多人想知道这如何与Sveltekit的服务器侧向性质一起使用 - 让我们以实践示例深入研究。如果您想查看整个项目

本文分为多个主题,随时查看您可能对!

的目录

查看内容at the original article!

从适配器静态

开始

要开始使用静态适配器,我们首先将其安装到一个全新的Sveltekit项目中,作为DEV依赖性:

npm i -D @sveltejs/adapter-static

然后,我们通过替换适配器配置来更改svelte.config.js文件以使用它:

- import adapter from '@sveltejs/adapter-auto';
+ import adapter from '@sveltejs/adapter-static';

现在,我们可以运行npm run build来构建项目。输出将是一个名为build/的文件夹,其中构成了我们的项目。

修复“所有路由必须完全可限制”错误

这是每个人使用的适配器静态遇到的错误。 解决错误的关键是了解动态路由的工作原理。

Sveltekit基本上有两种使用适配器静态时路由的模式。您可以路由到已知的,预先的路线或未知的动态路线。不同之处在于,已知的,预先的路由生成了代表该路由的HTML内容的关联的.html文件。例如,如果我们有一个预先介绍的路线:

src/routes/prerendered/+page.svelte
src/routes/prerendered/+page.js # contains "export const prerender = true;"

使用prerendered.html文件构建时,这将生成以下HTML结构。让我们看看构建后目录的样子:

tree build/ -L 1
build/
...
├── index.html
├── prerendered.html

相比

src/routes/dynamic/+page.svelte
src/routes/dynamic/+page.js # contains "export const prerender = false;"

...然后尝试构建应用程序,我们可以看到没有创建dynamic.html文件:

tree build/ -L 1
build/
...
├── index.html
# No dynamic.html file!

Sveltekit的默认值是假设所有路线都是动态的,因此您要么需要在root +layout.js中设置export const prerender = true以摆脱消息(明确将所有路由设置为PRERENDERABLE),要么启用 fall> fallback < /strong>选项。

在我们谈论后备选项之前,您可能会想知道,如果输出不存在,动态路线如何工作?答案是他们使用客户端渲染和一些服务器端路由魔术。如果我们查看fallback option的官方文档,我们可以阅读“后备页面是由Sveltekit从您的页面模板(例如App.html)创建的HTML页面,该页面将加载您的应用程序并导航到正确的路由。”

为了了解预先读者与动态路线的行为,让我们看一下图:

正如我们看到的,动态路线通过与基础Web服务器合作来工作。 Web服务器看到一个404错误(Beause在文件系统上不存在动态路由),而是加载了Sveltekit使用 sholdback 选项生成的后备页面。然后,此特殊页面将客户端渲染浏览器中的正确路径。这意味着一旦我们配置了后备选项,我们就不再需要将所有路线设置为PREREDER。

对于最终用户,至关重要的要点是,可以使用静态适配器呈现 PRERENDER的路线,从而获得性能和SEO效益。动态路线可以使用,甚至可以为每个用户加载个性化和动态数据,但不能使用SSR

设置后备配置

可以在svelte.config.js文件中完成Sveltekit中的后备设置:

- adapter: adapter(),
+ adapter: adapter({ fallback : '404.html' }),

现在,您需要告诉Web服务器遇到错误时加载404.html。例如,在Apache中,您可以使用配置:

ErrorDocument 404 /404.html

在我们的同伴存储库中,我们使用http-server NPM模块运行该项目,该项目默认为使用404.html作为404页面后备。

trailingSlash: 'always'选项将每个路由的构建输出的名称从/route-name.html更改为/route-name/index.html。对于某些服务器,这可以更好地工作,因为通常,如果您导航到/route-name/,大多数Web服务器都会在/route-name/文件夹中寻找index.html文件。根据我的经验,您通常希望通过在src/routes/+layout.js中设置以下内容来启用此问题:

export const trailingSlash = 'always';

使用Sveltekit作为传统水疗中心

将Sveltekit用作传统的单页应用程序(例如经典的“ Create React App”),我们需要Sveltekit到:

  1. 不要尝试prerender或ssr任何东西(即。
  2. 始终返回一个空的“ shell” html文件,该文件仅加载关联的JavaScript,然后处理所有繁重的举重。

为了实现这一目标,我们需要完全禁用SSR并完全预先启动。在src/routes/+layout.js中,我们设置了:

export const prerender = false;
export const ssr = false;

现在,您的应用程序就像任何水疗中心一样。您甚至可以忽略一些Sveltekit约定(例如,您不必将仅限浏览器代码与if(browser)包装,因为Sveltekit永远不会在服务器上运行您的组件)。但是,我不建议这样做,因为如果您想再次启用SSR,它可能会很快变得一团糟。

+server.js端点

在除静态一个适配器外,服务器端点将作为动态服务器端代码部署。在Vercel上,您将获得无服务器函数,在节点适配器上,您将在基于Polka的Express.js like服务器中获得一条路线。

但是,我们无法将动态服务器端端点编译到静态JavaScript文件中。 默认情况下,适配器静态将完全剥离 +server.js文件。

但是一个用例,我们可能希望从+server.js文件中生成静态输出,而Sveltekit通过export const prerender = true;选项公开了这一点。通过在端点文件中设置此功能,Sveltekit将在端点的构建时间创建一个输出文件。让我们用一个示例说明。如果我们有一个称为src/routes/randomNumber.json/+server.js的文件,看起来像这样:

import { json } from '@sveltejs/kit';

/** @type {import('./$types').RequestHandler} */
export function GET({ url }) {
    const randomNumber = Math.random();
    return json({
        randomNumber
    });
}

export const prerender = true;

构建应用程序并检查目录后,我们将看到创建一个randomNumber.json文件:

# npm run build
# ...
# tree build/ -L 1
build/
├── _app
├── favicon.png
├── index.html
└── randomNumber.json

如果我们打开文件,我们将看到这正是我们的期望 - 在构建时间生成的随机数:

{
    "randomNumber": 0.44040173838860297
}

虽然Prerender技术不允许我们个性化数据,但它对于在构建时间中从CMS或其他数据源进行预取数据仍然非常有用。每当基础数据更改时,我们都可以触发网站的重建以使用新数据进行更新。

+page.server.js端点应该仅在服务器上运行,那么它如何与apapter static一起使用? 默认情况下,与+server.js文件一样​​,服务器加载功能无法通过使用默认配置来工作。 +page.server.js代码将被删除。这意味着,如果您使用这些功能,一旦构建应用程序总是无法加载。不幸的是,Vite不了解这种限制(甚至我们正在使用的适配器),因此您无法使用npm run preview测试这种行为。 ,您可以通过在独立的Web服务器上,构建站点然后运行npm run build && npx http-server ./build来测试它。

但是,就像使用+server.js Function一样,有一种方法可以将+page.server.js加载函数与适配器静态使用,并且是相同的修复程序 - 您需要将它们设置为PRERENDERABLE。

PREREDERED负载功能将是什么样的?让我们进行实验。首先,让我们创建一个非常简单的组件,该组件显示来自src/routes/server-load/+page.svelte下的服务器加载函数的随机数:

<script>
    export let data;
</script>

<h1>
    A (maybe?) random number: {data.number}
</h1>

src/routes/server-load/+page.server.js下的随附的负载功能。请注意,我们在此处使用prerender = true,否则该功能将无法如前所述。

export const load = async () => {
    return {
        number: Math.random(),
    };
};

export const prerender = true;

现在让我们构建网站并查看输出:

# npm run build
# tree build -L 2
build
├── ...
└── server-load
    ├── __data.json
    └── index.html

除了生成的HTML文件外,我们还获得了一个__data.json文件。这是我们内置的负载功能的实际内容!让我们看看它包含的内容:

{
    "type": "data",
    "nodes": [
        null,
        {
            "type": "data",
            "data": [
                {
                    "number": 1
                },
                0.8636617558590938
            ],
            "uses": {}
        }
    ]
}

Sveltekit添加了一些样板,但我们可以毫无疑问地看到我们生成了一个数字。

此示例强调了使用适配器静态的服务器负载功能的局限性 - 构建后他们无法更新数据。以上功能将始终返回相同的数字:

这并不意味着服务器加载功能是没有用的 - 就像+server.js端点一样,您可以使用它们来构建静态数据,例如您最新博客文章的列表,然后在内容更改后重建网站。

+page.js加载功能

+page.js负载功能称为“通用”,因为它们在浏览器和服务器上都运行。在任何其他适配器中,这意味着通用负载函数将作为SSR的一部分在服务器上执行。如果执行任何获取调用,将在服务器和the results will be sent as serialized data to the client上执行这些调用。这样做是为了避免两次执行请求 - 一次在服务器上,一次在客户端上。

使用适配器静态构建时,+page.js负载功能将无法正常工作,而是运行两次:

  1. 一次(在服务器上)构建网站时 - 如果启用了SSR,则将此功能的输出烘烤到生成的HTML中。
  2. 每次用户加载页面时(在客户端上)。

这意味着+page.js负载功能是放置客户端逻辑的绝佳场所。例如,您可以使用它使用fetch加载动态用户数据。请记住,它将在构建过程中运行一次。对于具有某种用户登录系统的大多数应用程序,这意味着它很可能会渲染“已登录状态”,因为在服务器构建过程中没有用户集cookie或localstorage值。

SSR如何与静态站点一起使用?

SSR的工作原理与+page.server.js相似。在构建时间期间,Sveltekit将在+page.js+page.server.js中调用负载功能,并根据此结果生成HTML输出。除非您重建项目,否则此HTML输出将永远不会改变。此功能非常强大,因为这意味着我们可以借助SSR获得SEO的好处(Google和其他搜索引擎更喜欢完全渲染的HTML站点),以及可以在全球范围内部署的完全静态站点的速度。

有时我们不想要SSR。例如,如果您要构建一个客户端渲染的应用程序,该应用程序将处理复杂的逻辑客户端。在这种情况下,就像我们在“使用Sveltekit”作为传统水疗中心的章节中一样,将以下内容添加到您的src/routes/+layout.js文件中最容易禁用SSR。

export const ssr = false;

这将做的是防止Sveltekit生成HTML输出。

假设我们在src/routes/ssr/+page.svelte下有这样的基本路线

<h1>Hello world!</h1>

现在,让我们将输出与在src/routes/ssr/+page.js中设置export const ssr = true;开始进行比较。

# curl http://localhost:5173/ssr/
<!-- Some output omitted for brevity -->
<!DOCTYPE html>
<html lang="en">

<body data-sveltekit-preload-data="hover">
    <div style="display: contents">  
        <h1 data-svelte-h="svelte-1vv3a6r">
            Hello world!
        </h1> 
        <script>
            {
                    // SvelteKit hydration initialization
            }
        </script>
    </div>
</body>

正如我们所看到的,<h1>Hello world!</h1>是HTML输出的一部分。搜索引擎很高兴,因为他们可以轻松阅读内容,并且用户很高兴,因为浏览器会立即向其显示内容!

现在,让我们使用export const ssr = false;禁用SSR并进行比较:

# curl http://localhost:5173/ssr/
<!-- Some output omitted for brevity -->
<!DOCTYPE html>
<html lang="en">

    <body data-sveltekit-preload-data="hover">
        <div style="display: contents">
            <script>
            {
                    // SvelteKit hydration initialization
            }
            </script>
        </div>
    </body>
</html>

看起来相似,但是现在完全缺少<h1>标签!如果我们查看页面,它仍然会出现在浏览器中,但只有在Sveltekit为我们补充了页面。

预先登录(SSG)

让我们想象我们正在建立一个博客,该博客将部署在github页面等静态主机上。我们将使用{JSON}占位符API。首先,让我们编写+page.server.js加载功能,该功能将加载我们的博客文章,in src/routes/+page.server.js

export async function load({ params }) {
    const response = await fetch('https://jsonplaceholder.typicode.com/posts');
    const posts = await response.json();
    const first10posts = posts.slice(0, 10);
    return {
        posts: first10posts,
    };
}

export const prerender = true;

现在让我们在src/routes/+page.svelte中添加前端:

<script>
    export let data;
</script>
<h1>Blog posts</h1>

{#each data.posts as post}
    <h2>{post.title}</h2>
    <a href="/posts/{post.id}">Read more</a>
{/each}

这为我们提供了一个美丽的简约博客页面:

现在,让我们添加单个帖子的路线。在src/routes/posts/[id]/+page.server.js中:

export async function load({ params }) {
    const id = params.id;
    const response = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`);
    const post = await response.json();

    return {
        post
    };
}

export const prerender = true;

src/routes/posts/[id]/+page.svelte中的前端:

<script>
    export let data;
</script>

<a href="/">Back</a>
<br/>

<h1>{data.post.title} </h1>
<p>{data.post.body}</p>

这是一个帖子页面的输出:

现在,当我们构建项目时, sveltekit将爬网我们的索引页面并找到所有单独的博客文章。因此,在我们的输出中,它实际上将在我们的10个博客文章中提供完整的SSR支持!让我们通过对构建输出进行刷子来证明这一点:

# npm run build
# tree build -L 3
build
├── # ...
├── posts
│   ├── 1
│   │   ├── __data.json
│   │   └── index.html
│   ├── 2 # All posts have an index.html!
│   ├── 3
│   ├── 4
│   ├── 5
│   ├── 6
│   ├── 7
│   ├── 8
│   └── 9
│   ├── 10

这是一个非常强大的功能,它使Sveltekit成为SSG网站的粉丝范围!

但是,有一个问题 - 如果没有与我们的动态内容的链接,该怎么办?为了解决此问题,Sveltekit为我们提供了两种不同的解决方案 - 选择最适合您的用例的解决方案:

  1. Export an koude57 function that can generate a list of all your content on a per-route basis.
  2. svelte.config.js中设置config.kit.prerender.entries下的条目列表

dev模式上的注释

当我们使用npm run dev时,Sveltekit并不是真正知道我们已经配置了适配器静态。因此,Sveltekit的行为就像我们有任何动态适配器一样。例如,加载函数中的数据将在每个页面加载上重新加载,而不仅仅是在启动时一次。这很方便,因为如果您每次要更新数据时都必须重新启动DEV服务器,这将变得非常烦人,但是它可以给出错误的印象,以了解该站点在构建后的运作方式。

结论

虽然适配器静态的作用与其他适配器的作用不同,但它允许我们使用Sveltekit的许多功能功能,例如服务器端渲染(SSG)以及渲染动态路线,从而使其与其他方法一样强大,例如使用自定义VITE设置。实际上,这甚至更容易,因为您可以访问Sveltekit中存在的出色客户端路由器和数据加载功能!如果您的问题是“我应该将Sveltekit用于静态和/或水疗站点?” - 答案是是一个响亮的是!

您喜欢博客文章吗?有什么不清楚的吗?在下面发表评论!

Kelly SikkemaUnsplash < / p>上的横幅 /社交分享照片< / p>