MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Svelte路由系统搭建与优化

2021-02-063.6k 阅读

Svelte路由系统搭建基础

1. 认识Svelte路由的重要性

在现代前端应用开发中,路由系统是不可或缺的一部分。对于Svelte应用而言,一个良好的路由系统能够实现页面之间的平滑切换,为用户提供流畅的浏览体验。它使得应用能够根据不同的URL地址,加载并展示相应的组件内容,从而构建出单页应用(SPA)的架构。想象一下,当用户在浏览器地址栏输入不同的路径时,就如同在翻阅一本书的不同章节,而路由系统则负责准确无误地引导用户到达相应的内容页面。

2. 选择合适的路由库

在Svelte生态中,有几个知名的路由库可供选择,其中@sveltejs/kitsvelte - routing是较为常用的。@sveltejs/kit是Svelte官方推荐的框架,它集成了路由系统,为开发者提供了强大的功能,包括服务器端渲染(SSR)、静态站点生成(SSG)等。而svelte - routing则是一个轻量级的路由库,专注于路由功能本身,适用于简单的Svelte应用开发。

使用@sveltejs/kit

@sveltejs/kit以文件系统为基础来定义路由。在项目创建时,src/routes目录下的文件和文件夹结构决定了应用的路由配置。例如,在src/routes目录下创建一个about.svelte文件,这个文件对应的路由就是/about。如果要创建嵌套路由,可以通过创建子文件夹来实现。比如,在src/routes/blog目录下创建[slug].svelte文件,这里[slug]是动态参数,它可以匹配/blog/任意字符串这样的URL路径,用于展示不同博客文章的详情页。

# 创建一个新的@sveltejs/kit项目
npm create svelte@latest my - app
cd my - app
npm install
npm run dev

在上述代码中,我们首先使用npm create svelte@latest命令创建一个新的Svelte应用项目,并命名为my - app。然后进入项目目录,安装依赖并启动开发服务器。

使用svelte - routing

svelte - routing的使用方式相对较为传统,通过在代码中定义路由规则来实现。首先需要安装该库:

npm install svelte - routing

然后在主脚本文件中引入并使用:

<script>
    import { Router, Route, Link } from'svelte - routing';
    import Home from './Home.svelte';
    import About from './About.svelte';
</script>

<Router>
    <Route path="/" component={Home} />
    <Route path="/about" component={About} />
</Router>

<nav>
    <Link href="/">Home</Link>
    <Link href="/about">About</Link>
</nav>

在上述代码中,我们从svelte - routing库中引入了RouterRouteLink组件。Router是整个路由系统的容器,Route用于定义具体的路由规则,将特定的路径映射到相应的组件。Link组件则用于创建导航链接,当用户点击链接时,路由系统会根据链接的href属性值切换到对应的页面。

搭建基本路由结构

1. 创建页面组件

在开始搭建路由系统之前,我们需要先创建一些页面组件,这些组件将作为路由的目标页面。假设我们要创建一个简单的应用,包含首页、关于页面和联系页面。

首页组件(Home.svelte)

<script>
    // 首页组件逻辑
</script>

<main>
    <h1>Welcome to our website</h1>
    <p>This is the home page of our Svelte application.</p>
</main>

关于页面组件(About.svelte)

<script>
    // 关于页面组件逻辑
</script>

<main>
    <h1>About Us</h1>
    <p>We are a team dedicated to building amazing Svelte applications.</p>
</main>

联系页面组件(Contact.svelte)

<script>
    // 联系页面组件逻辑
</script>

<main>
    <h1>Contact Us</h1>
    <p>You can reach us at contact@example.com</p>
</main>

2. 配置路由

使用@sveltejs/kit配置路由

@sveltejs/kit项目中,我们在src/routes目录下创建相应的文件和文件夹。

创建src/routes/about.svelte文件,内容为关于页面组件的代码。

创建src/routes/contact.svelte文件,内容为联系页面组件的代码。

首页则对应src/routes/index.svelte文件,内容为首页组件的代码。

这样,路由系统会自动将/路径映射到首页,/about路径映射到关于页面,/contact路径映射到联系页面。

使用svelte - routing配置路由

在主脚本文件(例如main.js)中,我们这样配置路由:

<script>
    import { Router, Route, Link } from'svelte - routing';
    import Home from './Home.svelte';
    import About from './About.svelte';
    import Contact from './Contact.svelte';
</script>

<Router>
    <Route path="/" component={Home} />
    <Route path="/about" component={About} />
    <Route path="/contact" component={Contact} />
</Router>

<nav>
    <Link href="/">Home</Link>
    <Link href="/about">About</Link>
    <Link href="/contact">Contact</Link>
</nav>

在上述代码中,我们为每个页面组件都定义了对应的路由规则,并且通过Link组件创建了导航链接,方便用户在不同页面之间进行切换。

动态路由的实现

1. 动态路由的概念

动态路由允许我们根据不同的参数值,展示不同的页面内容。例如,在一个博客应用中,每篇博客文章都有一个唯一的标识符,通过动态路由,我们可以根据这个标识符展示对应的文章内容。动态路由使得应用能够灵活地处理各种变化的数据,提高了应用的通用性和可扩展性。

2. 在@sveltejs/kit中实现动态路由

@sveltejs/kit中,实现动态路由非常直观。假设我们有一个博客应用,要展示每篇博客文章的详情页。我们在src/routes/blog目录下创建[slug].svelte文件,其中[slug]就是动态参数。

<script context="module">
    export async function load({ params }) {
        const slug = params.slug;
        // 根据slug从数据库或API获取文章数据
        const article = await fetch(`/api/articles/${slug}`).then(res => res.json());
        return {
            props: {
                article
            }
        };
    }
</script>

<script>
    export let article;
</script>

<main>
    <h1>{article.title}</h1>
    <p>{article.content}</p>
</main>

在上述代码中,我们通过load函数获取动态参数slug,并根据这个参数从API中获取对应的文章数据。然后将文章数据作为props传递给组件,在组件中展示文章的标题和内容。

3. 在svelte - routing中实现动态路由

svelte - routing中,我们通过在Route组件的path属性中使用占位符来定义动态路由。

<script>
    import { Router, Route, Link } from'svelte - routing';
    import BlogArticle from './BlogArticle.svelte';
</script>

<Router>
    <Route path="/blog/:slug" component={BlogArticle} />
</Router>

<nav>
    <Link href="/blog/article - 1">Article 1</Link>
    <Link href="/blog/article - 2">Article 2</Link>
</nav>

BlogArticle.svelte组件中,我们可以这样获取动态参数:

<script>
    import { getCurrentRoute } from'svelte - routing';
    const { params } = getCurrentRoute();
    const slug = params.slug;
    // 根据slug从数据库或API获取文章数据
    const article = await fetch(`/api/articles/${slug}`).then(res => res.json());
</script>

<main>
    <h1>{article.title}</h1>
    <p>{article.content}</p>
</main>

在上述代码中,我们通过svelte - routing提供的getCurrentRoute函数获取当前路由的信息,从而获取到动态参数slug,然后根据slug获取文章数据并展示。

嵌套路由

1. 嵌套路由的需求场景

在一些复杂的应用中,我们需要展示多层次的页面结构,这时候嵌套路由就显得尤为重要。例如,在一个电商应用中,商品分类页面下可能有多个子分类页面,每个子分类页面又有对应的商品列表页面。通过嵌套路由,我们可以清晰地组织这些页面之间的关系,使应用的结构更加合理。

2. 在@sveltejs/kit中实现嵌套路由

@sveltejs/kit中,嵌套路由通过在src/routes目录下创建子文件夹来实现。假设我们有一个产品目录应用,产品分类下有不同的子分类。

我们先在src/routes/products目录下创建[category].svelte文件,用于展示某个产品分类的页面。

<script context="module">
    export async function load({ params }) {
        const category = params.category;
        // 根据category从数据库或API获取该分类下的子分类数据
        const subCategories = await fetch(`/api/products/${category}/sub - categories`).then(res => res.json());
        return {
            props: {
                subCategories
            }
        };
    }
</script>

<script>
    export let subCategories;
</script>

<main>
    <h1>{category}</h1>
    <ul>
        {#each subCategories as subCategory}
            <li><a href={`/products/${category}/${subCategory.slug}`}>{subCategory.name}</a></li>
        {/each}
    </ul>
</main>

然后在src/routes/products/[category]目录下创建[subCategory].svelte文件,用于展示某个子分类下的产品列表。

<script context="module">
    export async function load({ params }) {
        const category = params.category;
        const subCategory = params.subCategory;
        // 根据category和subCategory从数据库或API获取产品列表数据
        const products = await fetch(`/api/products/${category}/${subCategory}/products`).then(res => res.json());
        return {
            props: {
                products
            }
        };
    }
</script>

<script>
    export let products;
</script>

<main>
    <h1>{subCategory}</h1>
    <ul>
        {#each products as product}
            <li>{product.name}</li>
        {/each}
    </ul>
</main>

3. 在svelte - routing中实现嵌套路由

svelte - routing中,我们通过在Route组件中嵌套Route组件来实现嵌套路由。

<script>
    import { Router, Route, Link } from'svelte - routing';
    import ProductCategory from './ProductCategory.svelte';
    import ProductSubCategory from './ProductSubCategory.svelte';
</script>

<Router>
    <Route path="/products/:category">
        <Route path="/:subCategory" component={ProductSubCategory} />
        <ProductCategory />
    </Route>
</Router>

<nav>
    <Link href="/products/electronics">Electronics</Link>
    <Link href="/products/clothing">Clothing</Link>
</nav>

ProductCategory.svelte组件中,我们可以展示分类下的子分类链接:

<script>
    import { getCurrentRoute } from'svelte - routing';
    const { params } = getCurrentRoute();
    const category = params.category;
    // 根据category从数据库或API获取子分类数据
    const subCategories = await fetch(`/api/products/${category}/sub - categories`).then(res => res.json());
</script>

<main>
    <h1>{category}</h1>
    <ul>
        {#each subCategories as subCategory}
            <li><a href={`/products/${category}/${subCategory.slug}`}>{subCategory.name}</a></li>
        {/each}
    </ul>
</main>

ProductSubCategory.svelte组件中,我们可以获取子分类参数并展示产品列表:

<script>
    import { getCurrentRoute } from'svelte - routing';
    const { params } = getCurrentRoute();
    const category = params.category;
    const subCategory = params.subCategory;
    // 根据category和subCategory从数据库或API获取产品列表数据
    const products = await fetch(`/api/products/${category}/${subCategory}/products`).then(res => res.json());
</script>

<main>
    <h1>{subCategory}</h1>
    <ul>
        {#each products as product}
            <li>{product.name}</li>
        {/each}
    </ul>
</main>

Svelte路由系统的优化

1. 代码拆分与懒加载

随着应用规模的增大,加载所有的页面组件代码可能会导致初始加载时间过长。代码拆分与懒加载可以解决这个问题。

在@sveltejs/kit中实现代码拆分与懒加载

@sveltejs/kit默认支持代码拆分与懒加载。当我们创建页面组件时,@sveltejs/kit会自动将其代码进行拆分。例如,对于一个较大的博客文章详情页组件BlogArticle.svelte,在用户访问该页面之前,其代码不会被加载。只有当用户点击链接进入该页面时,才会加载对应的代码。

在svelte - routing中实现代码拆分与懒加载

svelte - routing中,我们可以使用动态导入来实现懒加载。

<script>
    import { Router, Route, Link } from'svelte - routing';
</script>

<Router>
    <Route path="/blog/:slug" component={() => import('./BlogArticle.svelte')} />
</Router>

<nav>
    <Link href="/blog/article - 1">Article 1</Link>
    <Link href="/blog/article - 2">Article 2</Link>
</nav>

在上述代码中,我们通过() => import('./BlogArticle.svelte')这种动态导入的方式,使得BlogArticle.svelte组件的代码在用户访问对应的路由时才会被加载,从而提高了应用的初始加载性能。

2. 预加载策略

虽然懒加载可以提高初始加载性能,但有时候我们可以提前预加载一些可能会用到的组件代码,以进一步提高用户体验。

在@sveltejs/kit中实现预加载

@sveltejs/kit提供了load函数的fetch选项来实现预加载。例如,在博客文章列表页面,我们可以预加载下一篇文章的组件代码。

<script context="module">
    export async function load({ params }) {
        const currentArticle = await fetch(`/api/articles/${params.slug}`).then(res => res.json());
        const nextArticleSlug = currentArticle.nextSlug;
        const prefetch = nextArticleSlug && {
            '/api/articles/' + nextArticleSlug: {
                method: 'GET'
            }
        };
        return {
            props: {
                currentArticle
            },
            fetch: prefetch
        };
    }
</script>

<script>
    export let currentArticle;
</script>

<main>
    <h1>{currentArticle.title}</h1>
    <p>{currentArticle.content}</p>
    {#if currentArticle.nextSlug}
        <a href={`/blog/${currentArticle.nextSlug}`}>Next Article</a>
    {/if}
</main>

在上述代码中,我们通过fetch选项预加载了下一篇文章的数据,当用户点击“Next Article”链接时,数据可以更快地展示出来。

在svelte - routing中实现预加载

svelte - routing中,我们可以通过监听路由变化事件,提前加载可能会用到的组件代码。

<script>
    import { Router, Route, Link } from'svelte - routing';
    import BlogArticle from './BlogArticle.svelte';
    let nextArticleComponent;
    Router.subscribe(({ to }) => {
        if (to.path.startsWith('/blog/') && nextArticleComponent === undefined) {
            import('./BlogArticle.svelte').then(module => {
                nextArticleComponent = module.default;
            });
        }
    });
</script>

<Router>
    <Route path="/blog/:slug" component={BlogArticle} />
</Router>

<nav>
    <Link href="/blog/article - 1">Article 1</Link>
    <Link href="/blog/article - 2">Article 2</Link>
</nav>

在上述代码中,当路由变化到以/blog/开头的路径时,我们提前加载BlogArticle.svelte组件的代码,以便用户访问博客文章页面时能够更快地展示内容。

3. 路由过渡效果优化

路由过渡效果可以让页面切换更加平滑和美观,提升用户体验。

在@sveltejs/kit中实现路由过渡效果

@sveltejs/kit可以通过CSS动画和Svelte的过渡指令来实现路由过渡效果。例如,我们可以在src/routes/+layout.svelte文件中添加过渡效果。

<script>
    import { fade } from'svelte/transition';
</script>

{#if $page}
    <div transition:fade="{{ duration: 300 }}">
        {#await $page.render()}
            <p>Loading...</p>
        {:then contents}
            {contents}
        {:catch error}
            <p>{error.message}</p>
        {/await}
    </div>
{/if}

在上述代码中,我们使用了Svelte的fade过渡效果,当页面切换时,会有一个淡入淡出的动画效果,持续时间为300毫秒。

在svelte - routing中实现路由过渡效果

svelte - routing中,我们可以通过在路由组件外部包裹一个带有过渡效果的组件来实现。

<script>
    import { Router, Route, Link } from'svelte - routing';
    import { fade } from'svelte/transition';
    import Home from './Home.svelte';
    import About from './About.svelte';
</script>

<Router>
    <div transition:fade="{{ duration: 300 }}">
        <Route path="/" component={Home} />
        <Route path="/about" component={About} />
    </div>
</Router>

<nav>
    <Link href="/">Home</Link>
    <Link href="/about">About</Link>
</nav>

在上述代码中,我们同样使用了fade过渡效果,使得页面在路由切换时产生淡入淡出的动画效果。

通过以上对Svelte路由系统的搭建与优化,我们可以构建出高性能、用户体验良好的前端应用。无论是选择@sveltejs/kit还是svelte - routing,都可以根据项目的需求和规模来灵活运用这些技术,为用户带来流畅、美观的浏览体验。同时,持续关注路由系统的优化,如代码拆分、预加载和过渡效果等方面,能够不断提升应用的质量和竞争力。