11_Vue路由

1. 什么是路由

​ 访问一个web应用(或者网站),需要有一个URL,这个URL可以通过HTTP协议 POST或者GET或 PUT或DELETE到服务端,当服务端接收到这个请求地址之后,通过规则匹配URL,然后转发到后端应用程序中的某个方法(函数)调用上,方法(函数)调用完毕之后会生成HTML文本,随后HTML文本会响应到浏览器中,浏览器完成HTML渲染,最终就看到了一个完整的页面。

​ 我们称这种将 URL转发到后端方法(函数)调用的过程叫做路由,匹配的规则集合就叫做路由表。

1.1 后端路由

​ 上面描述的场景是在后端完成的,所以也叫后端路由。这种后端路由是多页面的,页面在服务端生成好直接返回给浏览器,对SEO友好。缺点是页面由后端模板通过程序来维护和生成,维护起来既臃肿又麻烦。

1.2 前端路由

​ 有了前后端分离的开发模式后,后端只需要提供API来返回数据,前端通过Ajax获取数据后,再用一定的方式渲染到页面里,这样做的有点就是前后端分工明确,后端专注数据逻辑,前端专注在交互和可视化上。
​ 前端路由主要使用在单页面应用上(SPA single page web application),前端维护一个路由规则。当地址发生变化的时候,不再向服务端发送HTTP请求,而是通过前端的路由规则(路由表)来进行匹配,匹配后进行页面视图的更新,因为前端需要展现的视图本身就在前端,无需向后端索要,后端只需为前端提供数据即可。

​ 前面学习过 通过 Vue 中 component 的 is 来实现动态加载组件。前端路由与动态组件很相似,只不过是通过 vue-router插件来实现的,路由不同的页面事实上就是动态加载不同的组件。

2. vue-router

vue-router 是一个npm包,要使用它必须先安装它。官方文档

2.1 安装

 npm i vue-router -S  或 cnpm i vue-router -S

vue-router是一个vue插件,所以在实例化Vue对象之前,需要使用use方法进行加载。

import Vue from 'vue'
import App from '@/App'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap/dist/js/bootstrap'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const vm = new Vue({
    el: '#app',
    render: h => h(App)
})

2.2 简单使用

在 App.vue页面上有一个导航,放置两个链接 “首页"和"关于”, 默认显示首页内容,当点击关于的时候,显示关于页面。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

About.vue

<template>
  <div class="bg">
    这是 about 页面
  </div>
</template>
<script>
export default {}
</script>
<style scoped>
    .bg {
        background-color: darkseagreen;
    }
</style>

Home.vue

<template>
  <div class="bg">
    这是 Home页面
  </div>
</template>
<script>
export default {}
</script>
<style scoped>
    .bg {
        background-color: cornflowerblue;
    }
</style>

App.vue

<template>
  <div>
     <div>
      <router-link to="/">首页</router-link> |
      <router-link to="/about">关于</router-link>
    </div>
    <!-- 被路由规则匹配到的 组件将会被显示到这里 -->
    <router-view />
  </div>
</template>

main.js

import Vue from 'vue'
import App from '@/App'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap/dist/js/bootstrap'
import VueRouter from 'vue-router'
import Home from '@/views/Home'
import About from '@/views/About'
Vue.use(VueRouter)
// 路由表
const routes = [{
    path: '/',
    name: 'Home',
    component: Home
}, {
    path: '/about',
    name: 'About',
    component: About
}]

// 路由对象
const router = new VueRouter({
    routes // ES6语法  相当于  routes: routes
})

const vm = new Vue({
    router, // 配置路由对象
    el: '#app',
    render: h => h(App)
})

使用总结:

  1. npm 安装vue-router

  2. 导入 VueRouter插件,并使用 Vue.use(VueRouter) 加载插件

  3. 定义路由规则(路由表)

    路由表是一个数组,数组的每个元素就是一个 规则,其中包含了 匹配路径(path),路由名称 (name), 路由对应的组件(component).

    当 匹配到路由之后(可以按照path匹配,也可以按照name匹配), 就会自动渲染对应的组件。

  4. 实例化路由对象,在路由对象中"装入" 路由表对象

  5. 实例化Vue对象的时候,配置路由选项

<router-link to="/">首页</router-link>  
最终渲染出HTML代码 <a href="#/" class="router-link-active">首页</a>  ,to 对应路由表中的path, 

 <router-view /> 
 它相当于是一个占位符,一旦路由匹配到了,路由所对应的组件就会填充这个占位符。

3. 分离路由表

上面的路由表定义在了main.js中,应用一旦变得复杂,将会有很多路由,所以这里将路由表分离到单独的文件中进行维护。
在这里插入图片描述
src/router/index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '@/views/Home'
import About from '@/views/About'

Vue.use(VueRouter)

// 路由表
const routes = [{
    path: '/',
    name: 'Home',
    component: Home
}, {
    path: '/about',
    name: 'About',
    component: About
}]

// 路由对象
const router = new VueRouter({
    routes // ES6语法  相当于  routes: routes
})
export default router

src/main.js

import Vue from 'vue'
import App from '@/App'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap/dist/js/bootstrap'
// 会默认加载 index.js
import router from '@/router'
const vm = new Vue({
    router, // 配置路由对象
    el: '#app',
    render: h => h(App)
})

4. 路由模式

vue-router在实现单页面前端路由时,提供了两种方式:Hash模式和History模式。

4.1 Hash模式

当浏览器的URL发生变化的时候,总是会向服务器端发起请求。但是在SPA 应用中,路由的变化是不会向服务端发送请求的,这是可以通过 URL Hash 来实现的。

一个URL Hash是这样的:

http://localhost:3000/#/about

这种#号后面 hash 值的变化,并不会导致浏览器向服务器发出请求,浏览器不发出请求,也就不会刷新页面。另外每次 hash 值的变化,还会触发hashchange 这个事件,通过这个事件我们就可以知道 hash 值发生了哪些变化。vue-router的Hash模式就是通过这个监听这个hashchange事件来实现更新页面部分内容的操作的。

上面创建的路由对象并没有指定路由模式,默认使用的就是这种模式。

const router = new VueRouter({
	mode: 'hash',
    routes 
})

创建VueRouter的时候还有很多其它选项,详情可以查看参考文档

4.2 History模式

HTML5标准发布以后,多了两个 API,pushState 和 replaceState,通过这两个 API 可以改变 url 地址且不会发送请求。同时还有popstate事件。通过这些就能用另一种方式来实现前端路由了,但原理都是跟 hash 实现相同的。用了HTML5的实现,单页路由的url就不会多出一个#,变得更加美观。但因为没有 # 号,所以当用户刷新页面之类的操作时,浏览器还是会给服务器发送请求。为了避免出现这种情况,所以这个实现需要服务器的支持,需要把所有路由都重定向到根页面。如:

http://localhost:3000/about

const router = new VueRouter({
	mode: 'history',
    routes 
})

5. 动态路由

动态路由是指:需要某种模式的路由,都映射到同一个组件上。其实就是path上带有动态变化的路径参数,比如:

/users/:id  

这里的 :id 就是路径参数,是一个变化的值 如:

/users/123
/users/456

这两个路径都能与 /users/:id 匹配上,那么这两个路径都会被映射到同一个组件上。

实例:

点击用户管理,显示用户列表,点击详情显示详情页面
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

路由定义:

src/router/index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import Users from '@/views/Users'
import UserDetail from '@/views/UserDetail'

Vue.use(VueRouter)

// 路由表
const routes = [{
    path: '/users',
    component: Users
}, {
    path: '/users/:id',
    component: UserDetail
}]

// 路由对象
const router = new VueRouter({
    routes
})
export default router

src/views/Users.vue

<template>
  <div>
      <table class="table table-bordered">
      <thead>
        <tr>
          <th>#</th>
          <th>姓名</th>
          <th>年龄</th>
          <th>操作</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="(user,index) in users" :key="user.id">
          <th scope="row">{{ index +1 }}</th>
          <td>{{ user.name}}</td>
          <td>{{ user.age }}</td>
          <td><router-link :to="{path:`/users/${user.id}`,query: user}">详情</router-link> </td>
        </tr>
       
      </tbody>
    </table>
  </div>
</template>

<script>
let users=[
  {id:1001,name:'张三',age:20},
  {id:1002,name:'李四',age:21},
  {id:1003,name:'王五',age:22}
]
export default {
  data(){
    return {
      users
    }
  }
}
</script>

定义了一个用户数组,然后用表格显示这个列表。

<router-link :to="{path:`/users/${user.id}`,query: user}">详情</router-link>

router-link 参考文档

:to 指定路由, path 中带有用户ID参数, query 查询参数。最终被渲染为:

<a href="#/users/1002?id=1002&name=李四&age=21">详情</a>

vue-router 路径中的参数叫做 params(参数), 而 ?后面的叫做 query, 通常称为查询参数。

src/views/UserDetail.vue 显示用户详情

<template>
  <div class="panel panel-default">
    <div class="panel-heading">用户详情</div>
    <div class="panel-body">
        <p> id : {{ user.id}} </p>
        <p> 姓名 : {{ user.name}} </p>
        <p> 年龄 : {{ user.age}} </p>
        
    </div>
</div>
</template>

<script>
export default {
    data(){
      return {
        user:null
      }
    },
    created(){
      console.log(this.$route)
       // this.$route.params  路径参数
      this.user= this.$route.query  // 查询参数
    }
}
</script>

当路由加载完 UserDetail组件后(created),会为组件注入 r o u t e 对 象 , 这 个 对 象 中 包 含 了 路 由 的 信 息 , route对象,这个对象中包含了路由的信息, routeroute对象 ,对象中包含以下信息: 更多信息可以查看 API文档

  • $route.path 当前路由的路径
  • $route.params 路由路径中的参数
  • $route.query 查询参数即,?后面的数据

在组件被创建后,这个$route对象就被注入到 组件中了。所以在 created 钩子函数中可以访问这个 $route对象了。从这个对象中取出 查询参数,放到 data上。(根据实际需要,也可以直接在模板 中访问: {{ $route.query }})

src/App.vue

<template>
  <div style="margin: 20px">
     <div >
      <router-link to="/users">用户管理</router-link>
    </div>
    <router-view />
  </div>
</template>

6. 嵌套路由

应用程序中有两个模块组件,用户(Users)和 订单(Orders), 这两个组件是 App的子组件,通过路由控制,在指定的位置 (数字1标注) 显示着两个组件。

用户模块中,还有连个子组件 UserList(用户列表) 和 UserAdd(新增用户)需要通过路由来显示,他们将在 (数字2) 的位置切换显示。这两层路由具备了嵌套关系,这就是嵌套路由
在这里插入图片描述
在这里插入图片描述
src/router/index.js 路由表

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '@/views/Home.vue'
import User from '@/views/user/User.vue'
import UserAdd from '@/views/user/UserAdd.vue'
import UserList from '@/views/user/UserList.vue'
import Order from '@/views/order/Order.vue'
import OrderAdd from '@/views/order/OrderAdd.vue'
import OrderList from '@/views/order/OrderList.vue'

Vue.use(VueRouter)

// 路由表
const routes = [{
    path: '/',
    component: Home
}, {
    path: '/users',
    component: User,
    children: [{
        // 注意以不能以/开头, 如果以“/” 开头,会被认为是从根路径开始,子路由不用添加父路径,会自动添加父路径的前缀
        path: 'list',
        component: UserList
    }, {
        path: 'add',
        component: UserAdd
    }]
}, {
    path: '/orders',
    component: Order,
    children: [{
        path: 'list',
        component: OrderList
    }, {
        path: 'add',
        component: OrderAdd
    }]
}]

// 路由对象
const router = new VueRouter({
    routes
})
export default router

src/views/order/Order.vue

<template>
    <div class="panel panel-primary">
        <div class="panel-heading">订单管理</div>

        <div class="panel-body">
             <router-link to="/orders/list" tag="Button" class="btn btn-primary" >订单列表 </router-link> 
        <router-link to="/orders/add" tag="Button" class="btn btn-success">添加订单 </router-link>
            <router-view />
        </div>
    </div>
</template>

src/views/order/OrderAdd.vue

<template>
    <div>
        <h2>新建订单</h2>
         {{$route.path}}
    </div>
</template>

src/views/order/OrderList.vue

<template>
    <div>
        <h2>订单列表</h2>
         {{$route.path}}
    </div>
</template>

src/views/user/User.vue

<template>
    <div class="panel panel-success">
        <div class="panel-heading">用户管理</div>
        <div class="panel-body">
            <!-- 这里使用的是从根路径开始 -->
             <router-link to="/users/list" tag="Button" class="btn btn-primary" >用户列表 </router-link> 
             <router-link to="/users/add" tag="Button" class="btn btn-success">添加用户 </router-link>
            <router-view />
        </div>
    </div>
</template>

src/views/user/UserAdd.vue

<template>
    <div >
        <h2>添加用户</h2>
        {{$route.path}}
    </div>
</template>

src/views/user/UserList.vue

<template>
    <div>
        <h2>用户列表</h2>
         {{$route.path}}
    </div>
</template>

src/views/Home.vue

<template>
    <div class="panel panel-primary">
        <div class="panel-heading">首页</div>

        <div class="panel-body">
            <h2>这是首页</h2>
        </div>
    </div>
</template>

src/App.vue

<template>
  <div style="margin: 20px">
    <ul class="nav nav-tabs">
      <li  class="{active:$route.path=='/users'}"> 
        <router-link to="/users">用户管理</router-link> 
      </li>
      <li class="{active:$route.path=='/orders'}">
        <router-link to="/orders">订单管理</router-link>
      </li>
    </ul>
    <router-view />
  </div>
</template>

运行:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

7. 路由导航

页面的导航跳转可以使用两种方式

  • 声明式

就是前面使用的 生成 a链接标签,点击链接后发生路由导航跳转

  • 编程式

vue实例内部,通过 获取 路由实例对象(this.$router) ,然后调用 它的push方法来实现路路由导航跳转。

7.1 <router-link>

官方文档

其实这种方式内部依然是调用 路由对象的push方法来完成的。

7.2 编程式导航

路由对象的push方法API参考

// 字符串
router.push('home')

// 对象
router.push({ path: 'home' })

// 命名的路由
router.push({ name: 'user', params: { userId: '123' }})

// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})

const userId = '123'
router.push({ name: 'user', params: { userId }}) // -> /user/123
router.push({ path: `/user/${userId}` }) // -> /user/123
// 这里的 params 不生效,因为params用在动态路由的path上面,这个path不是动态路由
router.push({ path: '/user', params: { userId }}) // -> /user

replace 方法:push 很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。

声明式编程式
<router-link :to="…" replace>router.replace(…)

8 命名路由

在路由表中,通过name 为路由进行命名,如:

routes: [
    {
      path: '/user/:userId',
      name: 'user',
      component: User
    }
  ]

要链接到一个命名路由,可以给 router-linkto 属性传一个对象:

<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>

9. 命名视图

一个路由可以对应多个视图 router-view, 可以为每个视图设置一个名称,没有设置则为 default 视图。可以为每个 视图指定一个 组件
在这里插入图片描述

  • 1 视图名称为header
  • 2 视图名称没有指定,默认为 default
  • 3 视图名称为 footer
    在这里插入图片描述
    路由表 src/router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '@/views/Home.vue'
import Login from '@/views/Login.vue'
import Footer from '@/layout/Footer.vue'
import Header from '@/layout/Header.vue'

Vue.use(VueRouter)

// 路由表
const routes = [{
        path: '/',
        components: { //注意这里是 components
            default: Home,
            footer: Footer,
            header: Header
        }
    },
    {
        path: '/login',
        component: Login //渲染到默认的 route-view上
    }
]

// 路由对象
const router = new VueRouter({
    routes
})
export default router

src/views/Home.vue

<template>
    <div class="panel panel-primary" style="width:800px">
        <div class="panel-heading">首页</div>

        <div class="panel-body">
            <h2>这是首页</h2>
        </div>
    </div>
</template>

src/layout/Header.vue

<template>
    <div style="width:800px;height:100px;color:#FFFFFF;background:#CCCCCC">
        这是Header  <button class="btn btn-info" @click="toUser"> 显示用户页面 </button>
    </div>    
</template>
<script>
export default {
    methods:{
        toUser(){
            this.$router.push('/login') //调用push方法实现路由切换
        }
    }
}
</script>

src/layout/Footer.vue

<template>
    <div style="width:800px;height:100px;color:#FFFFFF;background:#CCCCCC">
        这是Footer    
    </div>    
</template>

src/views/Login.vue

<template>
    <div class="panel panel-success" style="width:800px">
        <div class="panel-heading">用戶登录</div>

        <div class="panel-body">
            <h2>用户登录</h2>
        </div>
    </div>
</template>

在这里插入图片描述
在这里插入图片描述

可以看到 当切换到 /login之后,路由表中 /login 对应的组件为 Login, 那么这个组件将会被渲染到默认的视图中,而名称为 footer和 header的 视图什么也不会显示。如果将 /login路由配置成:

    {
        path: '/login',
        components: {
            default: Login, //渲染到默认的 route-view上
            footer: Footer // 显示footer组件
        }
    }

此时显示的结果为:
在这里插入图片描述

10 重定向和别名

来自于 参考文档

10.1 重定向

当访问 /a的时候,重定向到 /b ,地址栏路径由 /a 变成 /b

{ path: '/a', redirect: '/b' }    // 重定向到 '/b'

 { path: '/a', redirect: { name: 'foo' }} // 重定向到命名路由

 { path: '/a', redirect: to => {
      // 方法接收 目标路由 作为参数
      // return 重定向的 字符串路径/路径对象
    }

重定向会导致 地址栏中的地址发生变化

10.2 别名

可以为一个路由配置多个别名,别名与 path等效

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

访问 /a 和 访问 /b 都会渲染组件A ,地址栏不会发生变化。

11 .路由组件传参

当一个路由被匹配后,加载组件并渲染到视图上。此时要想获取路由的参数,则需要通过自动注入的 “$router” 对象来获取,这样 $router就与组件耦合了。

可以将 路由参数 “映射” 到组件的 props 属性中。

例如:

User组件:

User.vue

export default {
	data(){
		return {}
	},
	props: ['id','name','query']
}

路由表:

 //布尔模式  如果 props 被设置为 true,route.params 将会被设置为组件属性
{ path: '/user/:id', component: User, props: true }

//对象模式 如果 props 是一个对象,它会被按原样设置为组件属性。当 props 是静态的时候有用。
{ path: '/promotion', component: Promotion, props: { id:123,name: 'zhangsan' } }

// 函数模式 你可以创建一个函数返回 props。这样你便可以将参数转换成另一种类型,将静态值与基于路由的值结合等等。URL /search?q=vue 会将 {query: 'vue'} 作为属性传递给 SearchUser 组件。
{ path: '/search', component: SearchUser, props: (route) => ({ query: route.query.q }) }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

paopao_wu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值