Day 14:动态 Route 对号入座

上篇完成了巢状路由的设置之後,紧接着新需求又出现了!接续会员後台的收藏纪录页面,我们要进一步让收藏的书单可以被购买:

  • 点击收藏纪录中的单笔书目资料时,会进入购买页面
  • 购买页面有独立连结,但不另新增在会员导览项目中,项目底部装饰线停留在收藏纪录上

在解决需求之前,我们先来认识一下动态路由要怎麽设定。

冒号「:」动态比对路由参数

先前我们对於路由的设定都是连名带姓的指定路径内容,就像是详细提供你家地址,好让任何人可以直接亲门踏户的找到你家。

然而有些情况下我们并无法完全预测到路径的走向,常见的情境例如要对应出 user name、product id 或是 query 查询内容,这些都是随机资料,无法在一开始就指明清楚;不过我们还是可以凭藉着「关键字」先概括出方向,再带入符合的内容作为路径参数。

以会员资料为例,我们可以参考 GitHub 在 profile 页面所显示的网址内容「 https://github.com/帐户名称 」,每个人登入後所看的的网址都会像是被客制化一样显示出自己的帐户名称,太神奇了吧!它怎麽知道现在是谁登入网页?

这就可以利用「:」来达成动态比对路由,甚至不需要再设定多层巢状路由,例如:

const router = new VueRouter({
  routes: [
    { 
		path: 'member/:userName',
        component: member
	}
  ]
})

通常会先将 userName (需为唯一值,否则会找到多位同名会员)作为 request 参数发送一支搜寻会员的 API,确认有该名会员时再导向包含 userName 片段的会员路径,因此路径可以是「member/mary」,又或者是「member/john」。当路由完成匹配时,userName 片段就会被设置为路由物件属性 $route.params 的参数值之一,因此还可以直接利用该属性在所有元件中的 <template> 显示出用户名称:

<p>User Name: {{ $route.params.userName }}</p>

问号「?」非强制比对路由参数

若某个路由参数对你而言是个可有可无的状态,则可以使用「?」进行非强制比对。

  • 使用问号:设置可以匹配「member/001」,也可以是「member/001/mary」,虽然导向的路径不同,但两者显示的都是同一个元件内容,因此无论 userName 是否存在都不会影响连结结果。

    path: 'member/:userId/:userName?',
    
  • 使用冒号:设置必须完整匹配「member/001/mary」,若连至「member/001」则无法成功显示页面。

    path: 'member/:userId/:userName',
    

设置动态路由

了解以上动态路由的设置方法之後,我们就可以马上应用来解决新需求!

首先,新增 Purchase.vue 元件,并设置动态路由。

  • router/index.js

    	{
        path: "/member",
        redirect: { name: "Profile" },
        component: MemberPage,
        children: [
          {
            path: "profile",
            name: "Profile",
            component: Profile,
          },
          {
            path: "collection",
            name: "Collection",
            component: Collection,
          },
          {
            path: "purchase/:bookName",
            name: "Purchase",
            component: Purchase,
          },
        ],
      },
    

接着在 Collection.vue 新增购买按钮或是导向连结,点击後触发路由导向 Purchase 元件,并且比对路由参数 bookName

  • 方法一:使用 Bootstrap 元件 <b-button> 建立购买按钮

    <ul>
      <li v-for="book in collections" :key="book.id">
        <b-button
          variant="outline-success"
    	  @click="$router.push({ name: 'Purchase', params: { bookName: book.name } })"
    	>
    	  Buy
        </b-button>
        <p>Book Name: <span>{{ book.name }}</span></p>
      </li>
    </ul>
    
  • 方法二:使用 <router-link> 建立导向连结

    <ul>
      <li v-for="book in collections" :key="book.id">
    	<router-link
    	  class="buy_button"
    	  :to="{ name: 'Purchase', params: { bookName: book.name } }"
    	>
    	  Buy
    	</router-link>
        <p>Book Name: <span>{{ book.name }}</span></p>
      </li>
    </ul>
    

collection

渲染路由参数

由於 Collection.vue 触发路由导向时,已将书本名称指定为路径需带上的 params 参数,而 Purchase.vue 在设置路由时,也透过「:」进行动态路由比对需匹配 bookName 才是有效路径,因此在 Purchase.vue 可以直接利用路由参数 $route.params 渲染出书本名称。

	<div class="Purchase">
    <h1>Purchase</h1>
    <div class="bill">
      <p>
        Name: <span>{{ $route.params.bookName }}</span>
      </p>
      <p>Quantity: <span>1</span></p>
      <p>Price: <span>$500</span></p>
    </div>
  </div>

试着点击任何一本书目的购买连结,都能依照不同的路径切换而即时更新书本名称;例如切换到第二本书,当前路径为「/member/collection/purchase/Book%202」,其中网址内的「%20」为原本书名「Book 2」中代表空白格的编码。
but link

active-class 应用

目前每个购买页面确实都有各自的独立连结了,并且因为购买页面也是隶属於 MemberPage 元件之下的子层巢状路由,所以即使没有另外新增在会员导览项目中,仍有共享到会员导览列。

不过还差那麽一点,由於购买流程是从收藏纪录延伸出去的动线,因此新需求中还需要符合让购买页面的底部装饰线与收藏纪录共用,相当於进入购买页面之後,装饰线仍停留在收藏纪录上,然而目前从画面中我们并无从得知使用者的当前位址。

作法很简单,原本 Purchase 路径为「path: "purchase/:bookName"」,只要再加上一段字串:

	{
    path: "/member",
    redirect: { name: "Profile" },
    component: MemberPage,
    children: [
      ...
      {
        path: "collection",
        name: "Collection",
        component: Collection,
      },
      {
        path: "collection/purchase/:bookName",
        name: "Purchase",
        component: Purchase,
      },
    ],
  },

在前篇曾介绍过 <router-link> props 中包含 exact-active-class 及 active-class,此处正是利用当初在 MemberPage 元件中直接取用 .router-link-active 设定装饰线样式,它会透过模糊比对为当前匹配的路由时加上该 class,因爲「collection/purchase/:bookName」在模糊比对之下与「collection」路径相似而符合资格,所以在 Purchase 路径之下会跟着显示装饰线样式。

此时若在 MemberPage 元件改用.router-link-exact-active,Purchase 就会因为路径不符合精准比对的结果,而无法渲染出相应样式。

参考资料


<<:  [Day14] 补充说明 – Cookie、Session和Token之Part1

>>:  第29天 - 文件审核系统(7)_审核端3

Day 2: LeetCode 978. Longest Turbulent Subarray

Tag:随意刷-每月挑战(2021.09.15) Source: 978. Longest Turb...

鬼故事 - 我们有通过国际资安 OOO 标准

鬼故事 - 我们有通过国际资安 OOO 标准 credit: AilinStock, DNDmeme...

【D9】厨具熟练後制作精致:使用历史资料的Kbar做MA(移动平均线)图

前言 当熟悉了历史资料,发现有更厉害的K线,这时候就要善用工具,好好的料理资料一番,这次我们来用K线...

[Day_4]Python 字串(1)

字串 在Python里扮演很重要的角色, 使用**单引号「'」与双引号「"」**所包含的文...

Day14-旧网站重写成Vue_5_多图片切换

昨天PO完文重看一下旧文才发现前天说要讲json做轮播,结果昨天先讲了tab…. 希望今天能讲完轮播...