1. 程式人生 > 其它 >Blazor和Vue對比學習(進階.路由導航二):佈局(母版/巢狀)

Blazor和Vue對比學習(進階.路由導航二):佈局(母版/巢狀)

單檔案元件框架中,當更改請求地址時,並不會引發頁面跳轉,而是由框架捕獲請求地址(在框架中我們稱之為路由),然後根據路由與元件的對映關係,在頁面的指定位置切換和顯示元件。在哪個位置顯示(稱之為路由出口),就是佈局要解決的問題。無論是Vue,還是Blazor,我們都能以母版頁的方式來理解和使用佈局。母版頁,就是佈局的整個頁面環境,我們可以在母版頁的指定位置,放一個佔位符,這個佔位符作為路由出口,Vue使用【<router-view />】元件作為佔位符,而Blazor中使用【@Body】指令。母版頁本質上也是元件,所以我們可以將“在路由出口”顯示的元件也成為母版頁,這樣就可以實現複雜的巢狀佈局。文字描述,比較晦澀,下面通過案例直接上手。

一、案例說明:如圖所示,首屏為左右結構,左側顯示文字連結“關於”和“動物”,右側是一個路由出口。點選“關於”時,直接在路由出口上顯示“About元件”。點選“動物”時,切換到Animal元件,Animal元件也是一個母版頁,上下結構,上方顯示文字連結“PandaDetail”和DogDetail“”,下方是動物元件的路由出口,點選上方連結時,將切換顯示“PandaDetail元件”和“DogDetail元件”。

  

二、Blazor實現(忽略CSS樣式)。檔案結構包括:App.razor(路由管理器並設定頂層母版)、MainLayout.razor(預設頂層母版)、Index.razor(首頁)、About.razor、Animal.razor(二層母版)、PandaDetail.razor、DogDetail.razor。

1、App.razor(路由管理器並設定頂層母版) 

<!--建立專案時自動建立立,基本沒有變化,對佈局來說,主要是指定的預設母版頁ManiLayout-->
<Router AppAssembly="@typeof(App).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
        <FocusOnNavigate RouteData
="@routeData" Selector="h1" /> </Found> <NotFound> <PageTitle>Not found</PageTitle> <LayoutView Layout="@typeof(MainLayout)"> <p role="alert">抱歉,未找到請求的頁面.</p> </LayoutView> </NotFound> </Router>

 2、MainLayout.razor(預設頂層母版) 

<!--將本元件轉為母版頁,LayoutComponentBase提供了Body屬性
Body為RenderFragment型別,作為佔位符渲染子元件,稱之為路由出口-->
@inherits LayoutComponentBase

<PageTitle>巢狀佈局</PageTitle>
<div class="page">
    <div class="sidebar">
        <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
            <h1>首頁</h1>
        </NavLink>
        <NavLink class="nav-link" href="about">
            <h1>關於</h1>
        </NavLink>
        <NavLink class="nav-link" href="animal">
            <h1>動物</h1>
        </NavLink>
    </div>
    <main>
        @Body
    </main>
</div>

3、Index.razor(首頁,本案例中沒有實際意義)

<!--路由為首頁,隱式使用預設母版頁MainLayout-->
@page "/"

<PageTitle>首頁</PageTitle>
<h1>這裡是首頁</h1>
@code{
}

4、About.razor

<!--路由為about,隱式使用預設母版頁MainLayout-->
@page "/about"

<PageTitle>關於</PageTitle>
<h3>這裡是關於頁About.razor</h3>
@code {
}

5、Animal.razor(二層母版)

<!--路由為animal,使用母版頁MainLayout。
注意,如果元件轉為母版頁,需要顯式指定母版頁,不能隱式使用預設母版頁。-->
@page "/animal"
@layout MainLayout
<!--將元件轉為母版頁,可以使用@Body佔位符-->
@inherits LayoutComponentBase

<PageTitle>動物</PageTitle>
<h3>這裡是動物頁Animal.razor</h3>
<NavLink class="nav-link" href="animal/panda-detail"><h5>熊貓詳情</h5></NavLink>
<NavLink class="nav-link" href="animal/dog-detail"><h5>狗狗詳情</h5></NavLink>
<main>
    @Body
</main>
@code {
}

6、PandaDetail.razor、DogDetail.razor,Animal的子頁面

<!--PandaDetail.razor-->
<!--路由animal/panda-detail,母版頁為Animal.razor-->
@page "/animal/panda-detail"
@layout Animal

<h3>這裡是熊貓詳情頁PandaDetail.razor</h3>
@code {
}

<!--DogDetail.razor-->
<!--路由animal/dog-detail,母版頁為Animal.razor-->
@page "/animal/dog-detail"
@layout Animal

<h3>這裡是狗狗詳情頁DogDetail.razor</h3>
@code {
}
  三、Vue的實現(忽略CSS樣式)。檔案結構包括:Router/index.js(路由管理器)、App.vue(頂層母版)、About.vue、Animal.vue(二層母版)、PandaDetail.vue、DogDetail.vue。

1、App.vue(頂層母版)

<template>
  <header>
    <h1><router-link to="/">首頁</router-link></h1>
    <h1><router-link to="/about">關於</router-link></h1>
    <h1><router-link to="/animal">動物</router-link></h1>
  </header>
  <main>
    <!--頂層路由出口-->
    <router-view></router-view>
  </main>
</template>

2、About.vue 

<!--普通元件-->
<template>
  <h2>這裡是關於頁About.vue</h2>
</template>

3、Animal.vue(二層母版) 

<template>
  <h2>這裡是關於動物頁Animal.vue</h2>
  <header>
    <h1><router-link to="/panda-detail">熊貓詳情</router-link></h1>
    <h1><router-link to="/dog-detail">狗狗詳情</router-link></h1>
  </header>
  <main>
    <!--第二層路由出口-->
    <router-view></router-view>
  </main>
</template>

4、PandaDetail.vue和DogDetail.vue

<!--普通元件-->
<template>
    <h2>這裡是熊貓詳情頁PandaDetail.vue</h2>
</template>

<!--普通元件-->
<template>
    <h2>這裡是狗狗詳情頁DogDetail.vue</h2>
</template>

5、Router/index.js(路由管理器),巢狀設定主要在路由管理器中進行

//路由使用了name屬性,用於指定路由的別名
//導航時即可以使用路徑path: <RouterLink to="/about">關於</RouterLink>
//也可以使用別名name: <RouterLink :to="{name:'about'}">關於</RouterLink>
//RouterLink和router-link一樣
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
  {
    path: '/',
    name: 'home', 
    component: ()=> import('../views/Index.vue'),
  },
  {
    path: '/about',
    name: 'about',
    component: () => import('../views/About.vue')
  },
  {
    path: '/animal',
    name: 'animal',
    component: () => import('../views/Animal.vue'),
    //Animal的子路由,以下路由的元件,在Animal元件的路由出口的切換顯示
    children:[
      {
        path:'/panda-detail',
        name:'panda-detail',
        component: () => import('../views/PandaDetail.vue')
      },
      {
        path:'/dog-detail',
        name:'dog-detail',
        component:() => import('../views/DogDetail.vue')
      }
    ]
  }
]

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes
})

export default router

四、總結:通過巢狀路由實現,應該可以明顯的感受到Blazor和Vue的路由和佈局差異:

  1. Blazor是一個向上的過程,通過指定母版頁,來確定母子巢狀關係。而Vue是一個向下的過程,通過指定子頁面,來確定母子巢狀關係。
  2. Blazor的佈局構件,包括了@page、@layout、@inherits LayoutComponentBase、@body。而Vue,就一個router-view,然後就是在路由配置檔案(Router/index.js)中進行配置。它們的區別和路由一樣,Blazor分散到各個元件中,而Vue集中到路由配置檔案中。