毫不费力地导航:用店面UI制作动态面包屑
#javascript #vue #typescript #nuxt

使用面包屑对复杂的站点可能是有益的,因为它为用户提供了对站点层次结构的清晰视觉理解,并且对于用户来说,用户更直接地追溯了他们的步骤,改善了他们的经验和参与度,同时降低了我们网站的弹跳率。

在这篇文章中,我们将在我们的综合指南中发现使用NUXT和店面UI创建直观的面包屑组件的分步过程。

表中的内容

面包屑在网络导航中的重要性

面包板跟踪在应用程序层次结构的上下文中显示Web应用程序中的当前位置,并允许用户轻松地返回到先前的页面或级别。它提供了用于协助导航的替代性视觉演示,通常是由>符号分隔的水平序列,以指示其后的文本的嵌套级别。

The levels of breadcrumbs

您可以像文件目录层次结构一样查看面包屑,其中root首先出现,当前页面链接出现最后一个。

面包屑有三种类型:位置,属性和基于路径。这篇文章将讨论使用其NUXT应用程序实现基于路径的面包库组件,并使用其路由机制和店面UI进行视觉显示。

Note :对于安装了VUE路由器的VUE应用程序,您可以在本文中轻松地重复使用一些修改的代码。

先决条件

如果您设置了NUXT应用程序并可以使用以下命令:
可以帮助您使用:

npx nuxi init breadcrumbs-demo

哪个breadcrumbs-demo是我们代码演示应用程序的名称。您可以根据自己的喜好更改为任何名称。

接下来,让我们通过安装其软件包和NUXT的tailWindCSS模块将店面UI添加到我们的项目中:

yarn add -D @nuxtjs/tailwindcss @storefront-ui/vue

您应该遵循Storefront UI instructions在NUXT产品中将parwindcss和店面UI设置。完成后,我们已经准备好了我们的教程!

了解基于文件的路由系统

NUXT使用VUE路由器和基于文件的系统来从/pages目录中的文件生成应用程序的路由。例如,如果您有以下文件结构:

\page
|--index.vue
|--\products
|-----index.vue
|-----details.vue

场景后面,NUXT引擎将自动构建以下路线:

  • \作为主页,带有pages\index.vue作为视图组件。它的名称将为index,与文件名相同。
  • \productspages\products\index.vue,并以products为名。
  • \products\details用于pages\products\details View,以products-details为单位,因为details嵌套在products中。

您可以使用路由器实例的getRoutes()方法查看路由。您还可以使用[]语法(例如[sku].vue)将路由命名为动态。在这种情况下,NUXT将生成动态路径为/:sku(),而:sku()是Regex模式VUE路由器将使用并将目标参数匹配并将目标参数提取到sku字段时。

太好了。现在,我们了解了路由系统在NUXT中的工作方式。我们可以建立面包屑机制,将URL分解成碎屑。

创建一个usebreadcrumbs可以组合

要构建面包屑,我们创建了一个useBreadcrumbs(),可以在其中执行以下操作:

  1. 请注意该路线的更改,特别是在namepathmeta属性上,而不是整个route对象。
  2. 将我们的breadcrumbs数组的默认值初始化为家庭路线。
  3. 立即触发观察者,因此我们还将在初始页面加载或页面刷新上计算面包屑。
  4. breadcrumbs作为组合的返回值。
  5. 我们仅在当前路线不在主页上时触发观察者。

useBreadcrumbs()的基本代码如下:

export const useBreadcrumbs = () => {
    const route = useRoute()

    const HOMEPAGE = { name: 'Home', path: '/' };
    const breadcrumbs:Ref<Array<{ name: string; path: string; }>> = ref([ HOMEPAGE ])

    watch(() => ({
        path: route.path,
        name: route.name,
        meta: route.meta,
        matched: route.matched,
    }), (route) => {
        if (route.path === '/') return;

        //TODO - generate the breadcrumbs
    }, {
        immediate: true,
    })

    return {
        breadcrumbs
    }
}

接下来,我们将实施如何从当前路线中计算面包屑,包括动态和嵌套方式。

处理动态和嵌套路线

要构建页面的面包屑,最好让当前的路线知道其父母是谁。但是,在Vue Router和Nuxt中,不幸的是,没有办法这样做。取而代之的是,我们可以通过符号/的最后一个索引递归切片面包屑的路径来构建面包屑的路径。

以我们的/products/about/keychain路径为例。我们将将其分解为以下路径:"/products/about/keychain""/products/about""/products"""。每条路径都是我们需要展示的面包屑。要获取这些面包屑的显示名称,我们需要执行以下操作:

  1. useRouter()实例中获取可用路线的列表。
  2. 我们将找到每条面包屑路径的匹配路线。
  3. 我们的停止条件是减少路径是主页。

我们针对getBreadcrumbs()的代码如下:

function getBreadcrumbs(currPath: string): any[] {
    //1. When we reach the root, return the array with the Home route
    if (currPath === '') return [ HOMEPAGE ];

    //2. Continue building the breadcrumb for the parent's path
    const parentRoutes = getBreadcrumbs(currPath.slice(0, currPath.lastIndexOf('/')));

    //3. Get the matching route object
    //TODO
    //4. Return the merged array with the new matching route
    return [
        ...parentRoutes,
        {
            path: currPath,
            //TODO
            name: currPath,
        }
    ]
}

我们目前将currPath返回为path,而name则用于Breadcrumb。尽管如此,我们必须基于NUXT的生成路由配置来检测如何检测匹配路由,包括动态路由和动态嵌套路由。让我们接下来做。

匹配路线的模式

使用匹配路由路径时,有许多与我们需要处理的动态路线有关的方案,包括:

  • 动态路线,例如/products/:id()
  • 在同一父母下的动态路线和静态路线,例如/products/:id()pages/produts/[id].vue)和/products/aboutpages/products/about.vue
  • 动态路线嵌套在另一个动态路线中,例如/products/:id()/:field()

最直接的方法是将路线的路径和当前路径分为隔离器/。然后,我们迭代元素,然后比较一个元素以查看它是否是相同的值,或者subpath是否以:开头,如以下isMathPatternPath中所示:

const isMathPatternPath = (pathA: string, pathB: string) => {
    const partsA = pathA.split('/');
    const partsB = pathB.split('/');

    if (partsA.length !== partsB.length) return false;

    const isMatch = partsA.every((part: string, i: number) => {
        return part === partsB[i] || part.startsWith(':');
    })

    return isMatch;
}

然后,我们在currPath上使用isMathPatternPathcurrPath上使用,并因此接收匹配的路由的数组,结果带有以下假设:

  • 如果有静态路线,并且在同一父上存在动态路线,则将是两者的匹配。
  • 静态路由将始终出现在这种匹配的路线数组中的动态路线之前(字母出现在符号之前,例如':':')
  • 匹配的阵列包含带有动态兄弟姐妹的静态路线的一个不止一个结果。在这种情况下,我们将使用===比较进行确切的匹配。否则,数组应包含一个结果。

因此,我们对getBreadcrumbs()的实施将如下:

function getBreadcrumbs(currPath: string): any[] {
    //1. When we reach the root, return the array with Home route
    if (currPath === '') return [ HOMEPAGE ];

    //2. Continue building the breadcrumb for the parent's path
    const parentRoutes = getBreadcrumbs(currPath.slice(0, currPath.lastIndexOf('/')));

    //3. Get the matching route object
    const founds = routes.filter(r => isMathPatternPath(r.path, currPath));
    const matchRoute = founds.length > 1 ? founds.find(r => r.path === currPath) : founds[0];

    //4. Return the merged array with the new matching route
    return [
        ...parentRoutes,
        {
            path: currPath,
            //TODO
            name: matchRoute?.meta?.breadcrumb || matchRoute?.name || matchRoute?.path || currPath,
        }
    ]
}

基于matchRoute,我们将使用meta.breadcrumb字段获取所需的名称,或者其名称,路径或currPath作为后备值。

我们可以通过以下代码更新我们的useBreadcrumbs()

export const useBreadcrumbs = () => {
    //...

    watch(() => ({
        path: route.path,
        name: route.name,
        meta: route.meta,
        matched: route.matched,
    }), (route) => {
        if (route.path === '/') return;

        breadcrumbs.value = getBreadcrumbs(route.path);
    }, {
        immediate: true,
    })

    //...
}

这样,我们的useBreadcrumbs()就可以使用了。让我们展示它!

与UI组件集成

我们将复制Storefront UI Breadcrumbs with a Home icon的代码,然后在我们的components/Breadcrumbs.vue中粘贴。

The Breadcrumbs code in Storefront UI doc site

script setup部分中,我们将将breadcrumbs更改为道具,如下:

const props = defineProps({
  breadcrumbs: {
    type: Array,
    required: true,
  },
});

示例代码带有每个面包屑,带有namelink。因此,我们需要查找item.link并用template部分中的item.path替换它们。另外,我们希望将SfLink作为NuxtLink渲染,以避免通过向每个SfLink添加:tag="NuxtLink"出现在template中,然后在script部分中添加:tag="NuxtLink"

import { resolveComponent } from 'vue';

const NuxtLink = resolveComponent('NuxtLink');

太好了。我们的Breadcrumbs组件已完成。

现在在/layouts/default.vue中,我们将从useBreadcrumbs()复合获得breadcrumbs,并将其传递给Breadcrumbs组件进行渲染,如下:

<template>
    <Breadcrumbs class="mt-4 ml-4" :breadcrumbs="breadcrumbs"/>
    <div class="h-px m-4 bg-neutral-200 divider"></div>
    <slot />
</template>
<script setup>
import { useBreadcrumbs } from '../composables/useBreadcrumbs';

const { breadcrumbs } = useBreadcrumbs();
</script>

最后,确保您在app.vue中具有以下代码:

<template>
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>

,仅此而已。当我们导航到页面时,该应用将显示相应的面包屑:

The screenshot of breadcrumbs component in nested page

使用元字段进行面包屑

您注意到,当前的面包屑使用匹配路由中定义的默认名称,有时只能阅读。在我们的实施中,我们将route.meta?.breadcrumb用作面包屑的名称。要定义meta.breadcrumb,我们将使用NUXT的内置方法definePageMeta如下示例:

<script setup>
/**pages/products/index.vue */
definePageMeta({
    breadcrumb: 'Products Gallery',
})
</script>

在构建时间,NUXT将将所需的页面的元数据合并到路线的meta中,我们将相应地显示面包屑:

The screenshot of formatted breadcrumbs component using meta field

请注意,您无法按照上述动态路线的方法定义元。相反,在useBreadcrumbs中,您可以观看route.params并从参数和相关数据(例如产品标题)中获取适当的名称。

概括

您可以找到工作代码here

在这篇文章中,我们探索了如何使用其内置路由器为我们的NUXT应用程序制作面包屑机制,并使用店面UI使用面包屑组件可视化它们。实现很简单,在具有多层嵌套动态和静态路由的更复杂的路由系统中可能不是最佳的。但是,这是您构建自己的面包屑系统的好起点,并记住,亲吻规则!

希望您发现这篇文章有帮助。如果您有任何疑问或建议,请在下面发表评论。我很想听听您的来信。

ð如果您想赶上我有时候,请在Twitter上关注我| Facebook

ð通过我的新书Learning Vue了解Vue。早期版本现在可以使用!

喜欢这篇文章还是发现它有帮助?分享ðð¼ð