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

news2024/11/13 11:09:45

本章概要

  • 命名路由
  • 命名视图
  • 编程式导航
  • 传递 prop 到路由组件
  • HTML 5 history 模式

14.5 命名路由

有时通过一个名称来标识路由会更方便,特别是在链接到路由,或者执行导航时。可以在创建 Router 实例时,在routes 选项中为路由设置名称。
修改 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'
import books from '../assets/books'

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

在根路径(/)的配置中,使用 redirect 参数将对该路径的访问重定向到命名的路由 news 上。当访问 http://localhost:8080/ 时,将直接跳转到 News 组件。
以下是重定向的另外两种配置方式:

{
  path:'/'
  // 指定目标路径
  redirect:'/news'
},
{
  // /search/screens -> /search?q=screens
  path:'search/:searchText',
    redirect:to => {
    	return { path:'/search',query:{q:to.params.searchText} }
    }
}

修改 App.vue ,在设置导航链接时使用命名路由。如下:

<template>
  <p>
    <router-link to="/">首页</router-link>
    <router-link :to="{ name: 'news' }">新闻</router-link>
    <router-link :to="{ name: 'books' }">图书</router-link>
    <router-link :to="{ name: 'videos' }">视频</router-link>
  </p>
  <router-view></router-view>
</template>

<script>
export default {
  name: 'App',
  components: {
  }
}
</script>

注意:to 属性的值现在是表达式,因此需要使用 v-bind 指令

修改 Books.vue ,也使用命名路由。如下:

<template>
    <div>
        <h3>图书列表</h3>
        <url>
            <li v-for="book in books" :key="book.id">
                <router-link :to="{ name: 'book', params: { id: book.id } }">{{ book.title }}</router-link>
            </li>
        </url>
        <!-- Book 组件在这里渲染 -->
        <router-view></router-view>
    </div>
</template>

<script>
// 导入 Books 数组
import Books from '@/assets/books'
export default {
    data() {
        return {
            books: Books
        }
    }
}
</script>

接下来可以再次运行项目,观察效果,测试效果和前面的例子完全一样。
在路由配置中,还可以为某个路径取个别名,例如:

routes:[
  { path:'/a', component: A, alias:'/b' }
]

“/a” 的别名是“/b”,当用户访问 “/b”时,URL会保持为“/b”,但是路由匹配是“/a”,就想用户正在访问“/a”一样。
别名的功能可以自由地将 UI 结构映射到任意的 URL ,而不受限于配置的嵌套路由结构。
注意别名和重定向的区别,对于重定向而言,当用户访问“/a”时,URL 会被替换成“/b”,然后匹配路由为“/b”。

14.6 命名视图

有时需要同时(同级)显示多个视图,而不是嵌套展示。例如,创建一个布局,有 header(头部)、sidebar(侧边栏)和 main(主内容) 3个视图,这时命名视图就派上用场了。可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。例如:

<router-view class="view header" name="header"></router-view>
<router-view class="view sidebar" name="sidebar"></router-view>
<router-view class="view main" name="main"></router-view>

没有设置名字的 router-view,默认为 default。
一个视图使用一个组件渲染,因此对于同一个路由,多个视图就需要多个组件。在配置路由时,使用 components 选项。代码如下:

const router = createRouter({
  history:createWebHashHistory(),
  routes:[
    {
      path:'/',
      components:{
        default:Main,
        header:Header,
        sidebar:Sidebar
      }
    }
  ]
})

可以使用带有嵌套视图的命名视图创建复杂的布局,这时也需要命名用到的嵌套 router-view 组件。
下面看一个设置面板的示例,如下:
在这里插入图片描述

Nav 是一个常规组件,UserSettings 是一个父视图组件,UserEmailsSubscriptions、UserProfile 和 UserProfilePreview 是嵌套的视图组件。
UserSettings 组件的模板代码类似如下形式。

<!-- UserSettings.vue -->
<div>
	<h1>User Settings</h1>
  <NavBar></NavBar>
  <router-view></router-view>
  <router-view name="helper"></router-view>
</div>

其它 3 个 组件的模板代码如下:

<!-- UserEmailsSubscriptions.vue -->
<div>
  <h3>UserEmails Subscriptions</h3>
</div>

<!-- UserProfile.vue -->
<div>
  <h3>Edit your profile</h3>
</div>

<!-- UserProfilePreview.vue -->
<div>
  <h3>Preview of your profile</h3>
</div>

在路由配置中按上述布局进行配置。如下:

{
  path:'/settings',
  component:UserSettings,
  children:[{
    path:'emails',
    component:UserEmailsSubscriptions
  },{
    path:'profile',
    components:{
      component:{
        default:UserProfile,
        helper:UserProfilePreview
      }
    }
  }]
}

继续例子,将图书详情信息修改为与 Books 视图统计显示。
编辑 App.vue ,添加一个命名视图。如下:

<template>
  <p>
    <router-link to="/">首页</router-link>
    <router-link :to="{ name: 'news' }">新闻</router-link>
    <router-link :to="{ name: 'books' }">图书</router-link>
    <router-link :to="{ name: 'videos' }">视频</router-link>
  </p>
  <router-view></router-view>
  <router-view name="bookDetail"></router-view>
</template>

<script>
export default {
  name: 'App',
  components: {
  }
}
</script>

修改 router 目录下的 index.js 文件,删除 Books 组件的嵌套路由配置,将 Book 组件路由设置为顶层路由。如下:

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'
import books from '../assets/books'

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

至于 Books 组件内的 router-view ,删不删除都不影响 Book 组件的渲染。为了代码的完整性,可以将这些无用的代码注释删除。

Book.vue

<template>
    <p> 图书ID:{{ book.id }} </p>
    <p> 标题:{{ book.title }} </p>
    <p> 描述:{{ book.desc }} </p>
</template>

<script>
import Books from '@/assets/books'
export default {
    data() {
        return {
            book: {}
        }
    },
    created() {
        this.book = Books.find((item) => item.id == this.$route.params.id);
        this.$watch(
            () => this.$route.params,
            (toParams) => {
                console.log(toParams)
                this.book = Books.find((item) => item.id == toParams.id);
            }
        )
    }
}
</script>

运行项目,可以看到当单击一个图书链接时,图书的详细信息在 Books 视图同级显示了,如下:
在这里插入图片描述

14.7 编程式导航

除了使用 router-link 创建 a 标签定义导航链接,还可以使用 router 的实例方法,通过编写代码来导航。
要导航到不同的 URL ,可以使用 router 实例的 push() 方法,router.push() 方法会向 history 栈添加一个新的记录,所以当用户单击浏览器后退按钮时,将回到之前的 URL 。
当单击 router-link 时,router.push() 方法会在内部调用,换句话说,单击 router-link :to=“…” 等同于调用 router.push() 方法。
router.push() 方法的参数可以是字符串路径,也可以是位置描述对象。调用形式很灵活,代码如下:

// 字符串路径
router.push('home')
// 对象
router.push({path:'home'})
// 命名的路由
router.push({name:'user',params:{userId:'123'}})
// 带查询参数,结果是 /register?plan=private
router.push({path:'register',query:{plan:'private'}})
// 使用 hash,结果是 /about#team
router.push({path:'/about',hash:'#team'})

需要注意的是,如果提供了 push,params 会被忽略。那么对于 /book/:id 这种形式的路径调用 router.push() 的方式也需要改变。一种是通过命名路由,;一种是在 path 中提供带参数的完整路径。如下:

const id = 1;
// /book/1
router.push({ name:'book',params:{id:book.id} })
// /book/1
router.push({ path:`book/${id}` })

router.push() 方法和所有其他的导航方法都返回一个 Promise ,允许等待直到导航完整,并知道结果是成功还是失败。
继续前面的例子,修改 Books.vue ,用 router.push() 方法替换 router-link 。如下:

<template>
    <div>
        <h3>图书列表</h3>
        <url>
            <li v-for="book in books" :key="book.id">
                <!-- <router-link :to="{ name: 'book', params: { id: book.id } }">{{ book.title }}</router-link> -->
                <a href="#" @click.prevent="goRoute({ name: 'book', params: { id: book.id } })">
                    {{ book.title }}
                </a>
            </li>
        </url>
        <!-- Book 组件在这里渲染 -->
        <!-- <router-view></router-view> -->
    </div>
</template>

<script>
// 导入 Books 数组
import Books from '@/assets/books'
export default {
    data() {
        return {
            books: Books
        }
    },
    methods: {
        goRoute(location) {
            //当单击的URL中的参数id与当前路由对象参数id值不同时,才调用$router.push方法
            if (location.params.id != this.$route.params.id)
                this.$router.push(location)
        }
    }
}
</script>

说明:

  • 在组件实例内部,可以通过 this.router 访问路由实例,进而调用 this.router.push() 方法。
  • this.router 表示全局的路由对象,包含了用于路由跳转的方法,其属性 currentRouter 可以获取当前路由对象;this.route 表示当前路由对象,每一个路由都有一个 route 对象,可以获取对应的 name、path、params、query 等属性。

replace() 方法对应的声明式路由跳转为 《router-link :to=“…” replace》。
也可以在调用 push() 方法时,在位置对象中指定属性 replace:true 。如下:

router.push({ path:'/home',replace:true })
// 相当于
router.replace({ path:'/home' })

replace() 方法与 push() 方法用法相同,此处不再赘述。
与 window.history 对象的 forward() 、back() 、go() 方法对应的 router 实例的方法如下:

router.forward()
router.back()
router.go(n)

14.8 传递 prop 到路由组件

在组件中使用 route 会导致与路由的紧密耦合,这限制了组件的灵活性,因为它只能在某些 URL 上使用。虽然这并不一定是坏事,但是可以用一个 props 选项来解耦。如下:

const User = {
  template:'<div>User {{ $route.params.id }}</div>'
}
const routes = [{ path:'/user/id',component:User }]

可以为 User 组件,来避免硬编码 route.params.id。修改后的代码如下:

const User = {
  props:[ 'id' ],
  template:'<div>User{{ id }}</div>'
}
const routes = [{ path:'/user/id',component:User,props:true }]

在配置路由时,新增一个 props 选项,将它的值设置为 true 。当路由到 User 组件时,会自动获取 route.params.id 的值作为 User 组件的 id prop 的值。
对于带有命名视图的路由,必须为每个命名视图定义 props 选项。如下:

const routes = [
  {
    path:'/user/:id',
    component:{default:User,sidebar:Sidebar},
    props:{default:true,sidebar:false}
  }
]

当props 是一个对象时,它将按原样设置为组件 props ,这在 props 是静态的时候很有用。如下:

const routes = [
  {
    path:'promotion/from-newsletter',
    component:Promotion,
    props:{newsletterPopup:false}
  }
]

也可以创建一个返回 props 的函数,可以将参数转换为其他类型,或者将静态值与基于路由的值相结合。如下:

const routes = [
  {
    path:'search',
    component:SearchUser,
    props:route => ({ query:route.query.q })
  }
]

访问 URL:/search?q=vue ,会将 {query:‘vue’} 作为prop 传递给 SearchUser 组件。
尽量保持 props 函数为无状态的,因为它只在路由更改时计算。

14.9 HTML 5 history 模式

前面的例子中使用的是 hash 模式,该模式是通过调用 createWebHashHistory() 函数创建的,这会在 URL 中使用 “#” 标识要跳转目标的路径,如果你觉得这样的 URL 很难看,影响心情,那么可以使用 HTML 5 history 模式。
HTML 5 history 模式是通过调用 createWebHistory() 函数创建的。如下:

import { createRouter,createWebHistory } from 'vue-router'
const router = createRouter({
  history:createWebHistory(),
  routes:[
    // ...
  ]
})

继续前面的例子,修改 router 目录下的 index.js 文件,将路由改为 HTML 5 history 模式。如下:

import { createRouter, createWebHistory } 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'
// import books from '../assets/books'

export default createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      redirect: {
        name: 'news'
      }
    },
    {
      path: '/news',
      name: 'news',
      component: News,
    },
    {
      path: '/books',
      name: 'books',
      component: Books
    },
    {
      path: '/book/:id',
      name: 'book',
      components: {bookDetail: Book},
    },
    {
      path: '/videos',
      name: 'videos',
      component: Videos,
    },
  ]
})

再次运行项目,所有的 URL 都没有“#”了。如下:
在这里插入图片描述

不过 history 模式也有一个问题,当浏览器地址栏中直接输入 URL 或刷新页面时,因为该 URL 是正常的 URL,所以浏览器会解析该 URL 向服务器发起请求,如果服务器没有针对该 URL 的响应,就会出现 404 错误。

在 HTML 5 history 模式下,如果是通过导航链接来路由页面,Vue Router 会在内部截获单击事件,通过 JavaScript 操作 window.history 改变浏览器地址栏中的路径,在这个过程中并没有发起 HTTP 请求,所以就不会出现 404 错误。

如果使用 HTML 5 history 模式,那么需要在前端程序部署的 Web 服务器上配置一个覆盖所有情况的备选资源,即当 URL 匹配不到任何资源时,,返回一个固定的 index.html 页面,这个页面就是单页应用程序的主页面。

Vue Router 的官网给出了一些常用的 Web 服务器的配置,网址:https://router.vuejs.org/guide/essentials/history-mode.html

如果使用 Tomcat 作为前端程序的 Web 服务器,可以在项目根目录下新建 WEB-INF 子目录,在其下新建一个 web.xml 文件。代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
						http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" 
	id="WebApp_ID" version="4.0">
  	<error-page>
      <error-code>404</error-code>
      <location>/index.html</location>
    </error-page>
</web-app>

按照上述配置后,Tomcat 服务器就不会再返回 404 错误页面,对于所有不匹配的路径都会返回 index.html 页面。

提示:
在基于 Vue 脚手架项目的开发中,内置的 Node 服务器本身也支持 HTML 5 history 模式,所以开发时一般不会出现问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/60506.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用Unity实现FXAA

用Unity实现FXAAFXAA是现代的常用抗锯齿手段之一&#xff0c;这次我们来在Unity中从零开始实现它。 首先我们来看一个测试场景&#xff0c;我们在Game视角下将scale拉到2x&#xff1a; 可以看到画面的锯齿比较严重&#xff0c;下面我们将一步一步地实现FXAA&#xff0c;消除锯…

BDD - SpecFlow ExternalData Plugin 导入外部测试数据

BDD - SpecFlow ExternalData Plugin 导入外部测试数据引言SpecFlow ExternalData 插件支持的数据源Tags实践创建一个 Class Libary Project添加 NuGet Packages添加测试数据源文件CSV 文件Excel 文件添加 Feature 文件实现 Step Definition执行Scenario 导入测试数据源Scenari…

深入URP之Shader篇4: Depth Only Pass

Depth only pass unlit shader中包含了一个Depth Only Pass&#xff0c;这个pass的代码在Packages\com.unity.render-pipelines.universal\Shaders\DepthOnlyPass.hlsl中。这是一个公共pass&#xff0c;几乎所有的URP shader都会包含这个pass。本篇说一说这个pass的作用以及实…

Ubuntu映射到Windows网络驱动器

将虚拟机Ubuntu映射到Windows网络驱动器中&#xff0c;我们需要Ubuntu的网络和主机网络处于同一网段下&#xff0c;然后使Ubuntu具备共享文件功能&#xff0c;最后在windows下添加网络地址。 将Ubuntu设置和主机同一网段 查看主机网络信息 在虚拟机中 选择编辑-- 虚拟网络编…

Java的字符串String

文章目录什么是字符串String类的声明为什么我们的String是不可变的为什么String类用final修饰String的创建字符串比较相等关于Java中的比较关于字符串不同赋值操作对应的内存分配那对象如何进行比较内容字符串常量池StringTalbe的位置字符串常见的操作拼接操作获得字符串的子串…

事件驱动的微服务、CQRS、SAGA、Axon、Spring Boot

事件驱动的微服务、CQRS、SAGA、Axon、Spring Boot 学习构建分布式事件驱动的微服务、CQRS、事件溯源、SAGA、事务 课程英文名&#xff1a;Event-Driven Microservices, CQRS, SAGA, Axon, Spring Boot 此视频教程共10.0小时&#xff0c;中英双语字幕&#xff0c;画质清晰无…

一个带有楼中楼的评论系统数据库设置思路

前言 有个需求&#xff0c;需要实现百度贴吧那样能评论帖子中某一楼的评论里的评论 分析 说起来有点拗口&#xff0c;其实这个评论系统分为4个部分&#xff1a; 主题&#xff08;楼主发布的帖子&#xff09;直接返回楼主的评论&#xff08;从帖&#xff09;&#xff1a;直接…

(11)点云数据处理学习——Colored point cloud registration(彩色点注册)

1、主要参考 &#xff08;1&#xff09;官网介绍 Colored point cloud registration — Open3D 0.16.0 documentation 2、原理和实现 2.1原理 本教程演示了使用几何形状和颜色进行配准的ICP变体。实现了[Park2017]算法。颜色信息锁定沿切平面的对齐。因此&#xff0c;该算法…

Yocto创建自己的分区(基于STM32MP1)

Yocto创建自己的分区&#xff08;基于STM32MP1&#xff09; 前几章节我们分析了machine class里面几篇关键的class&#xff0c;还有machine conf里面的inc文件&#xff0c;大致的创建分区的流程都比较清晰了&#xff0c;本章节动手实际操作一把&#xff0c;创建一个自己的分区…

Unity中的协程

一、什么是协程 协程(Coroutines) 是一种比线程更加轻量级的存在&#xff0c;也被称为用户态线程一个进程可以拥有多个线程&#xff0c;一个线程可以拥有多个协程协程并不会增加线程&#xff0c;它在线程中运行&#xff0c;通过分时复用的方式运行多个协程&#xff0c;其切换代…

《Spring 5.x源码解析之Spring AOP 注解驱动使用及其实现原理》

《Spring 5.x源码解析之Spring AOP 注解驱动使用及其实现原理》 学好路更宽&#xff0c;钱多少加班。---- mercyblitz 一、前言 大家好&#xff0c;欢迎阅读《Spring 5.x源码解析》系列&#xff0c;本篇作为该系列的第二篇&#xff0c;重点介绍Spring AOP在注解驱动编程模式上的…

基于J2EE的大型视频影音系统的设计与实现

目 录 毕业设计&#xff08;论文&#xff09;任务书 I 摘 要 II ABSTRACT III 第1章 绪 论 1 1.1 课题的提出 1 1.1.1 Web2.0浪潮进一步影响全球互联网发展 1 1.1.2 视频分享成为2.0浪潮的最新爆发点 1 1.2 系统研究目的 2 1.3 系统设计目标 2 第2章 关键技术介绍 4 2.1 网页…

C#使用策略模式或者委托替代多IfElse判断和Switch语句

这篇文件介绍使用设计模式中的策略模式和委托来解决多个IfElse判断语句和Switch语句&#xff0c;这种替换方式在其他语言也一样可以做到&#xff0c;比如PHP、JavaScript、Python或者Java等。 这里以C#为例进行演示。 需要为一个程序编写计算方法&#xff0c;根据标签名称来决定…

【华为上机真题 2022】TLV解码

&#x1f388; 作者&#xff1a;Linux猿 &#x1f388; 简介&#xff1a;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;Linux、C/C、云计算、物联网、面试、刷题、算法尽管咨询我&#xff0c;关注我&#xff0c;有问题私聊&#xff01; &…

abc280

D 解法1&#xff0c;直接暴力&#xff0c;答案一定在2~1e6里面或者k本身&#xff08;如果k是个质数的话&#xff09; #include<bits/stdc.h> using namespace std; signed main() {long long k;cin>>k;for(long long i1;i<2000010;i) {k/__gcd(k,i);if(k1) {co…

在Linux中,使用Docker,安装es和kibana

1.部署单点es 1.1.创建网络 因为我们还需要部署kibana容器&#xff0c;因此需要让es和kibana容器互联。这里先创建一个网络&#xff1a; # 创建一个网络&#xff1a;es-net docker network create es-net# 查看本机的网络 docker network ls# 删除一个网络&#xff1a;es-ne…

Allegro如何缩放数据操作指导

Allegro如何缩放数据操作指导 Allegeo上可以缩放数据,尤其是在做结构时候非常有用,具体操作如下 以下图为例,需要把这个数据缩小0.5倍 点击Create Detail命令 Option里面选定一个层面,比如放在Board Geomertry,silkscreen top层 Scaling Factor输入0.5 Find选择所有 …

UE5 中 LiveLink 的开发全流程教程

注意&#xff0c;需要有源代码版本的 Unreal Engine&#xff0c;而不是从游戏 Launcher 中下载的 Unreal 版本。 本文使用是 Unreal Engine 5.1 版本。关于一些基础 API 介绍&#xff0c;可以参考之前的一篇。 起点 可以将 Engine\Source\Programs\BlankProgram 作为模板拷贝…

虚拟机搭载Linux · VMware + Ubuntu 部署 路线参考(20.04.5)

提前回家&#xff0c;要部署OS的实验环境。感谢广源同学给予的帮助和支持~ 电脑文件系统进行了整理&#xff0c;重型文件大部分转移到移动硬盘上。 &#xff08;解压了好久然后我找到镜像源了呜呜没发过来&#xff09; 一、VMware 16 安装 VMware虚拟机安装Linux教程(超详细)…

详解 Spring Boot 项目中的日志文件

目录 1. 日志的作用 2. 自定义日志打印 2.1 日志的基本格式 2.2 得到日志对象 2.3 使用日志对象提供的方法&#xff0c; 打印自定义的日志内容 2.4 日志框架的说明 3. 日志的持久化 3.1 配置日志文件的文件名 3.2 配置日志文件的保存路径 3.3 持久化日志的特性 4. 日…