十四、使用 Vue Router 开发单页应用(2)

本章概要

  • 动态路由匹配
    • 查询参数
  • 路由匹配语法
    • 参数的自定义正则表达式
    • 可重复参数
    • 可选参数
  • 嵌套路由

14.2 动态路由匹配

实际项目开发时,经常需要把匹配某种模式的路由映射到同一个组件。例如,有一个 Book 组件,对于所有 ID 各不相同的图书,都可以使用这个组件来渲染,这可以使用路径中的动态段(dynamic segment) 来实现。
继续《十四、使用 Vue Router 开发单页应用(1)》的项目,修改 App.vue ,使用 router-link 组件添加两个导航链接。代码如下:




在 components 目录下新建 Book.vue wen文件。如下:




接下来编辑 router 目录下的 index.js 文件,导入 Book 组件,并添加动态路径 /book/:id 的路由配置,如下:

import { createRouter, createWebHashHistory } from 'vue-router'
import Home from '@/components/Home'
import News from '@/components/News'
import Books from '@/components/Books'
import Videos from '@/components/Videos'
import Book from '@/components/Book'

export default createRouter({
  history: createWebHashHistory(),
  routes: [
    {
      path: '/',
      component: Home,
    },
    {
      path: '/news',
      component: News,
    },
    {
      path: '/books',
      component: Books,
    },
    {
      path: '/videos',
      component: Videos,
    },
    {
      path:'/book/:id',
      component:Book
    }
  ]
})

在终端窗口执行 npm run serve 命令,运行项目,打开浏览器,出现 图书1和图书2链接,单击其中任意一个。结果如下:
十四、使用 Vue Router 开发单页应用(2)_第1张图片

在同一个路由中可以有多个参数,它们将映射到 route.params 中的相应字段,如下表所示:

模式 匹配路径 route.params
/user/:username /user/evan {username:‘evan’}
/user/:username/post/:post_id /user/evan/post/123 {username:‘evan’,post_id:‘123’}

除了 route.params 外,route 对象还提供了其它有用信息,如 route.query (如果 URL 中有查询参数)、route.hash 等。

14.2.1 查询参数

URL 中带有查询参数的形式为 /book?id=1,这在传统的 Web 应用程序中很常见,根据查询参数想服务端请求数据。在单页应用程序开发中,也支持路径中的查询参数。

修改 App.vue 文件




修改 Book.vue




修改 index.js

import { createRouter, createWebHashHistory } from 'vue-router'
import Home from '@/components/Home'
import News from '@/components/News'
import Books from '@/components/Books'
import Videos from '@/components/Videos'
import Book from '@/components/Book'

export default createRouter({
  history: createWebHashHistory(),
  routes: [
    {
      path: '/',
      component: Home,
    },
    {
      path: '/news',
      component: News,
    },
    {
      path: '/books',
      component: Books,
    },
    {
      path: '/videos',
      component: Videos,
    },
    {
      path:'/book',
      component:Book
    }
  ]
})

运行项目,单击“图书1”链接。查询参数演示结果如下:
十四、使用 Vue Router 开发单页应用(2)_第2张图片

14.3 路由匹配语法

大多数应用程序使用静态路由(如/news)和动态路由(如/book/1)就可以满足应用的需求,不过 Vue Router 也提供了更加强大的参数匹配能力。要匹配任何内容,可以使用自定义参数正则表达式,方法是在参数后面的圆括号中使用正则表达式。

14.3.1 参数的自定义正则表达式

当定义一个如 “:id”的参数时,Vue Router 在内部使用正则表达式“([^/]+)”(至少有一个不是斜杠 / 的字符)从 URL 中提取参数。
假设有两个路由 /:orderId 和 /:productName ,它们将匹配完全相同的 URL ,要想区分它们,最简单的方法是在路径中添加一个静态部分来区分。代码如下:

const routes = [
  // 匹配 /o/3549
  { path:'/o/:orderId' },
  // 匹配 /p/books
  { path:'/p/:productName' }
]

假设要限定 orderId 只能是数字,而 productName 可以是任何值,那么在参数 orderId 后的圆括号中使用正则表达式来说明。代码如下:

const routes = [
  // /:orderId 只能匹配数字
  { path:'/o/:orderId(\\d+)' },
  // /:productName 匹配任何值
  { path:'/p/:productName' }
]

14.3.2 可重复参数

可以使用修饰符“*”(零个或多个)、“+”(一个或多个)将参数标记为可重复的。如下:

const routes = [
  // /:chapters -> 匹配 /one,/one/two./one/tow/three,etc
  { path:'/:chapters+' }
  // /:chapters -> 匹配 /,/one,/one/two./one/tow/three,etc
  { path:'/:chapters*' }
]

这将给出一个 params 数组而不是字符串,并且在使用命名路由时也需要传递一个数组。如下:

// given { path:'/:chapters*',name:'chapters' },
router.resolve({ name:'chapters',params:{chapters: [] } }).href
// 结果:/
router.resolve({ name: 'chapters',params:{ chapters:['a','b'] }}).href
// 结果:/a/b
// given { path:'/:chapters+',name:'chapters' }
router.resolve({ name:'chapters',params:{ chapters:[] } }).href
// 因为 chapters 是空,这将抛出一个错误

还可以通过将 “*”和“+”添加到右括号后,与自定义正则表达式结合使用。

const router = [
  // 只匹配数字
  { path:'/:chapters(\\d+)+' },
  { path:'/:chapters(\\d+)*' }
]

14.3.3 可选参数

还可以使用“?”将参数标记为可选的。如下:

const routers = [
  // 匹配 /users 和 /users/posva
  { path:'/users/:userId?' }
  // 匹配 /users 和 /users/42
  { path:'/users/:userId(\\d+)?' }
]

14.4 嵌套路由

在实际应用场景中,一个界面 UI 通常由多层嵌套的组件组合而成,URL 中的各段也按某种结构对应嵌套的各层组件。
十四、使用 Vue Router 开发单页应用(2)_第3张图片

路径 user/:id 映射到 User 组件,根据 ID 的不同,显示不同的用户信息。ID 为 1 的用户单击链接 user/1/profile ,将在用户1的视图中渲染 Profile 组件;单击 user/1/posts ,将在用户1的视图中渲染 Posts组件。

继续 14.2 节中的例子(将例子恢复为动态段),当单击“图书”链接时,以列表形式显示所有图书的书名,进一步单击单个书名链接,在 Books 视图中显示图书的详细信息。这可以通过嵌套路由来实现。
在 assets 目录下新建一个 books.js 文件,里面是图书数据。如下:

books.js

export default [
    { id: 1, title: '标题1', desc: '描述1' },
    { id: 2, title: '标题2', desc: '描述2' },
    { id: 3, title: '标题3', desc: '描述3' },
]

这里硬编码了图书数据,只是为了演示需要,真实场景中,图书数据应该是通过 Ajax 请求从服务端加载得到。
修改 Books.vue,以列表方式显示图书数据,添加导航链接,并使用 router-view 指定 Book 组件渲染的位置。如下:

Books.vue




注意删除 App.vue 中的图书1 和图书2 的 router-link 的配置。
修改 router 目录下的 index.js 文件,增加嵌套路由的配置。如下:

import { createRouter, createWebHashHistory } from 'vue-router'
import Home from '@/components/Home'
import News from '@/components/News'
import Books from '@/components/Books'
import Videos from '@/components/Videos'
import Book from '@/components/Book'

export default createRouter({
  history: createWebHashHistory(),
  routes: [
    {
      path: '/',
      component: Home,
    },
    {
      path: '/news',
      component: News,
    },
    {
      path: '/books',
      component: Books,
      children: [
        { path: '/book/:id', component: Book }
      ]
    },
    {
      path: '/videos',
      component: Videos,
    },
  ]
})

说明:

  • 要在嵌套的出口(即 Books 组件中的 router-view)中渲染组件,需要在 routes 选项的配置中使用 children 选项。children 选项只是路由配置对象的另一个数组,如同 routes 本身一样,因此,可以根据需要继续嵌套路由。
  • 以“/”开头的嵌套路径被视为根路径。如果导航链接设置的是 /books/book/id 这种形式,那么这里配置路径时,需要去掉“/”,即 {path:‘book/:id’,component:Book}

Book.vue





在终端窗口中运行项目,打开浏览器,单击“图书”链接后,任选一本图书,结果如下:
十四、使用 Vue Router 开发单页应用(2)_第4张图片

在实际场景中,当单击某本图书链接时,应该向服务器发起 Ajax 请求来获取图书详细数据,于是想到在 Book 组件中通过生命周期钩子函数来实现,然而,这行不通。

因为当两个路由都渲染同一个组件时,如从 book/1 导航到 book/2 时,Vue 会复用先前的 Book 实例,比起销毁旧实例再创建新实例,复用会更加高效。但是这就意味着组件间的生命周期钩子不会再被调用,所以也就无法在生命周期构钩子中去根据路由参数的变化更新数据。

要对同一组件的路由参数更改做出响应,只需监听 route.params 即可。
修改 Book.vue ,当路由参数变化时,更新图书详细数据。如下:

Book.vue




说明
(1)只有路由参数发生变化时,route.params 的监听才会被调用,这意味着第一次渲染 Book 组件时,通过 route.params 的监听器是得不到数据的,因此在 created 钩子中先获取第一次渲染时的数据。当然,也可以向 watch 方法传入一个选项对象作为第3个参数,设置 immediate 选项参数为 true ,使监听器回调函数在监听开始后立即执行,即不需要在 created 钩子中先获取一次数据。如下:

created(){
  // this.book = Books.find((item) => item.id == this.$route.params.id);
  this.$watch(
    () => this.$route.params,
    (toParams) => {
      this.book = Books.find((item) => item.id == toParams.id);
    }
  )
}

(2)route.params 监听器回调函数的 toParams 参数表示即将进入的目标路由的参数,该函数还可以带一个 previousParams 参数,表示当前导航正要离开的路由的参数。

运行项目,单击不同的图书链接将显示对应图书的详细信息,如图:
十四、使用 Vue Router 开发单页应用(2)_第5张图片

除了监听 route 对象外,还可以利用 Vue Router 中的导航守卫(navigation guard):beforeRouteUpdate,可以把它理解为是针对路由的一个钩子函数。
修改 Book.vue 删除 route.params 监听器,改用 beforeRouteUpdate 来实现。如下:




beforeRouteUpdate 在当前路由改变,但是该组价被复用时调用,它有两个常用的参数。to 表示即将进入的目标路由位置对象;from 表示当前导航正要离开的路由位置对象。此处只用到了参数 to。

你可能感兴趣的:(#,Vue.js,3.0,从入门到实战,动态路由匹配,路由匹配语法,嵌套路由)