跳到主要内容

路由

示例文件接到 Vue的组件


一、Vue路由

1、什么是路由?

后端路由:对于普通的网站,所有的超链接都是URL地址,所有的URL地址都对应服务器上对应的资源;

前端路由:对于单页面应用程序来说,主要通过URL中的hash(#号)来实现不同页面之间的切换,并不会向后端发送请求。同时,hash有一个特点:HTTP请求中不会包含hash相关的内容;所以,单页面程序中的页面跳转主要用hash实现;

在单页面应用程序中,这种通过hash改变来切换页面的方式,称作前端路由(区别于后端路由);

承接到Vue的组件的示例文件:👇

现在有Home,Goods和Users子组件,我们在点击跳转按钮的时候,页面通过<Component :is="currentPage" /> 改变了,但是地址url没变,这就是单页面应用。

这样有一个问题就是没法分享给他人,因为地址是相同的,但是看到的内容不同。还有一点就是没有回退功能。

如何解决这个问题?及用到了vue的路由。

2、路由的基本使用

在vue中使用路由需要用到vue-router的插件。

1、按照vue-router插件

npm i vue-router -S

2、引入并注入路由到vue

import Router from 'vue-router'

Vue.use(Router);

3、创建一个路由实例,参数为一个对象,这个配置对象中有一个属性:routes,表示 路由的匹配规则

let router = new Router({
routes : [
{ path: '/home', component: Home }, //当地址为/home时,显示Home组件
{ path: '/goods', component: Goods },
{ path: '/users', component: Users }
]
});

routes 属性是一个数组,数组中的每一项都是一条匹配规则。

每个路由规则,都是一个对象,这个规则对象,身上,有两个必须的属性:

path:表示监听的路由链接地址

component:表示如果路由是前面匹配到的 path ,则展示 component 属性对应的那个组件。

4、将路由规则对象router注册到vue实例

new Vue({
el: "#box",
data: {},
methods: {},
// 将路由对象注册到vue实例
router
});

5、一旦路由规则匹配成功,就会在vue-router 提供的标签router-view 上显示出来,这个标签就是匹配路由成功后component对应的组件应该显示的位置。

<div>
<button @click="goPage(nav.name)" v-for="nav in navs">{{nav.text}}</button>
<router-view></router-view>
</div>

然后来看看界面:

3、router-link属性

我们之前的超链接是由a标签代替的,如果要跳转那么并且每次href属性都要加上#,这样太麻烦。

vue-router官方提供了一个router-link 的属性可以实现路由的跳转。

<!--app.html-->
<div>
<router-link tag="button" :to="nav.name" v-for="nav in navs">{{nav.text}}</router-link>
<router-view></router-view>
</div>

我们通过检查可以知道,router-link 会默认渲染为一个a 标签。

但是我们也可以将其改变成别的标签,使用 tag="span" 属性可以将默认的a标签转换成span标签:

<router-link to="/login" tag="span">登陆</router-link>

4、路由嵌套

我们现在想在/home路径下再接子路由a和b,形成嵌套路由/home/a或者/home/b形式的路由。

在路由配置规则数组中使用 children 属性,实现子路由功能。

let router = new Router({
routes: [
// 路由重定向
{ path: '/', redirect: '/home' },
{
path: '/home',
component: Home,
// 使用children实现子路由
children: [
{ path: 'a', component: A },
{ path: 'b', component: B }
]
},
{ path: '/goods', component: Goods },
{ path: '/users', component: Users }
]
});

注意:子路由的匹配规则前面不要加 /

然后在Home的template中加入<router-view></router-view>即可。

来看现象:

给路由起别名

我们还可以给路由起个别名,这样在有些路由特别长的时候就很方便。

起别名的方式就是往路由匹配规则加个name属性即可。

let router = new Router({
routes: [
{ path: '/', redirect: '/home' },
{
path: '/home',
component: Home,
name: 'h',
children: [
{ path: 'a', component: A, name: 'a' },
{ path: 'b', component: B, name: 'b' }
]
}, //当地址为/home时,显示Home组件
{ path: '/goods', component: Goods, name: 'g' },
{ path: '/users', component: Users, name: 'u' }
]
});

那么我们在使用router-link跳转的时候,属性to就是动态的:

<router-link tag="button" :to="{name:'a'}" v-for="nav in navs">{{nav.text}}</router-link>

然后重定向redirect也可以使用name:

{ path: '/', redirect: {name:'h'} },

5、多视图路由

之前我们是在Home中加载A,B组件,现在有如下需求:

我们在加载/home/one路由的时候,没有名字的router-view加载A组件,有名字的router-view加载B组件;

在加载/home/two路由的时候,没有名字的router-view加载A2组件,有名字的router-view加载B2组件;

这种在Home中显示多个router-view的方式就叫做多视图路由。

首先在Home的template中添加router-view:

<div>
<h1>首页</h1>
<router-view></router-view>
<router-view name="b"></router-view>
</div>

接着配置路由:

let router = new Router({
routes: [
{ path: '/', redirect: '/home' },
{
path: '/home',
component: Home,
name: 'h',
children: [{
path: 'one',
components: {
b: B,
default: A
},
name: 'a'
},
{
path: 'two',
components: {
b: B2,
default: A2
},
name: 'b'
}
]
}, //当地址为/home时,显示Home组件
{ path: '/goods', component: Goods, name: 'g' },
{ path: '/users', component: Users, name: 'u' }
]
});

由于一个router-view要加载的有两个路由,所以component变成components,复数,然后是个对象,有name=b的就是b:B组件,没有name的就是default:A组件。

演示:

6、编程式导航

编程式导航就是通过js来实现页面跳转(之前都是通过标签router-link来跳转的),比如如果用户登录成功跳转道首页,这个判断的功能只能通过js来实现。

我在app.html添加一个按钮:

<button @click='goHome'>跳转到首页</button>

然后在app.js里面实现点击事件:

通过父组件.$router.push('路由地址') 或者是父组件.$router.push({name:'路由别名'})的方式进行跳转。

methods: {
goHome() {
this.$router.push('/home');
this.$router.push({ name: 'h' });
}
},

this.$router.pushthis.$router.replace的区别:

push会保留跳转前的地址,而replace不会。通过名字也可以看出端倪。

还有this.$router.go(1) 执行浏览指针的前进或者后退次数。

7、路由高亮显示

我们在点击登陆和注册链接的时候,可不可以让被点击的链接高亮显示呢?

方式一:

我们可以看到在激活的路由上面,会使用一个叫做router-link-active 的类,我们通过修改这个类的属性就可以让这个链接高亮显示了。

.router-link-active {
color: red;
background-color: yellow;
}

方式二:

可以通过路由的构造选项 linkActiveClass 来全局配置需要激活的路由样式。

var routerObj = new VueRouter({
routes: [{
path: '/',
redirect: '/login'
}, {
path: '/login',
component: login
}, {
path: '/register',
component: register
}],
// 自定义路由构造选项 linkActiveClass
linkActiveClass: 'myClass'
});

8、路由切换添加动画效果

既然 router-view 是显示组件的,那么我们可以给 router-view 来添加动画效果:

<style>
.v-enter,
.v-leave-to {
opacity: 0;
}

.v-enter-active,
.v-leave-active {
transition: opacity 0.5s ease;
}
</style>

<transition mode="out-in">
<router-view></router-view>
</transition>

9、给路由传递参数

方式一:问号传参

我们可以在路由中,使用查询字符串的方式给路由传递参数。

<div>
<h1>商品列表页面</h1>
<ul>
<router-link tag='li' :to="{name:'d',query:{gid:goods.goodsID}}" v-for="goods in list">
<h4>商品名:{{goods.name}}</h4>
<p>价格:{{goods.price}}</p>
</router-link>
</ul>
</div>

当然上面传递的方式就类似于传统的问号传参方式。比如 /detail?gid=4 这样的方式传递,如果想写在to里面就用上面的方式。

给路由传递参数后,我们并不需要修改路由匹配规则

我们给路由传递参数就是为了拿到参数,那么如何获取参数呢?

在我们的组件模板对象里面,可以使用this.$route.query 的方式获取我们传入的参数对象。

export let Detail = {
template : require("./index.html"),
data(){
return {
goodsid : 0
}
},
// 加载后获取路由参数
mounted(){
let goodsid = this.$route.query.gid
this.goodsid = goodsid
}
}

方式二:路径传参

首先在路由匹配规则中,使用占位符代替路由传递时的参数。

path 中的 :newGid表示如果以后路由后面跟内容了,那么内容的属性就是gid。

{path : '/detail/:newGid',component:Detail,name:"d"},

然后在传递参数时,就不是用query了,而是params。

<div>
<h1>商品列表页面</h1>
<ul>
<router-link tag='li' :to="{name:'d',params:{newGid:goods.goodsID}}" v-for="goods in list">
<h4>商品名:{{goods.name}}</h4>
<p>价格:{{goods.price}}</p>
</router-link>
</ul>
</div>

这时候,传过去时就不是问号的形式,而是路径的形式,如:/detail/4

获取的时候也不是query而是params,this.$route.params

export let Detail = {
template : require("./index.html"),
data(){
return {
goodsid : 0
}
},
// 加载后获取路由参数
mounted(){
let newGoodsid = this.$route.params.newGid;
this.goodsid = newGoodsid;
}
}

方式三:编程式导航传参

上面的路由传参都是采用to传参,也就是标签传参,怎么使用编程式导航传参?

其实和to是一样的:

this.$router.push({name:'d',query:{gid:goods.goodsID}})
this.$router.push({name:'d',params:{newGid:goods.goodsID}})

10、vue路由守卫

什么是路由守卫?简单来说就是路由在跳转之前的验证,当满足条件时才会进行跳转。

路由守卫也称导航守卫,分为全局守卫路由守卫组件守卫

10.1、全局守卫

注册全局守卫应该在路由模块暴露出去之前定义,使用router.beforeEach(function(to,from,next){})来注册一个全局守卫。

参数:

  • to:代表目标路径对象

  • from:来源路径对象

  • next:用于决定是否继续进行跳转。该方法参数有三种情况:

    当next()函数不传参数或者传入true的时候 则允许正常跳转;

    当next()函数传入false时 会中断跳转(阻止跳转);

    当next()函数中传入路径时或者对象时(比如:{name:'xxx'})则会重定向到指定路径。

定义在router上的路由守卫 ,全局范围内有效,只要有路径跳转就会触发该守卫。

假如我现在从首页/home跳转到/home/two,根据随机数是否大于0.5决定是否跳转:

import Vue from 'vue'
import Router from 'vue-router'

import { Home } from './pages/Home'
import { Goods } from './pages/Goods'
import { Users } from './pages/Users'

import { A } from './components/A'
import { B } from './components/B'
import { A2 } from './components/A2'
import { B2 } from './components/B2'

Vue.use(Router);

let router = new Router({
routes: [
{ path: '/', redirect: '/home' },
{
path: '/home',
component: Home,
name: 'h',
children: [{
path: 'one',
components: {
b: B,
default: A
},
name: 'a'
},
{
path: 'two',
components: {
b: B2,
default: A2
},
name: 'b'
}
]
}, //当地址为/home时,显示Home组件
{ path: '/goods', component: Goods, name: 'g' },
{ path: '/users', component: Users, name: 'u' }
]
});

// 当有路由跳转的时候,就会执行这个函数
router.beforeEach(function(to, from, next) {
console.log(to, from);
let num = Math.random();
console.log(num);
next(num > 0.5 ? true : false);
});

export { router };

可以看到只有随机数大于0.5的时候,页面才真正发生了变化,跳转到了/home/two:

我们把路径对象展开可以看到所有有关路径的信息:

全局守卫还有一个router.afterEach(function(to,from){}) ,还是在路由跳转之前执行,它在决定跳转后 以及真正跳转前执行,其中形参 to from同上。

10.2、路由守卫

路由守卫就是针对单个路由对象配置的守卫。

假如我在users组件配置路由守卫,那么只有跳转到users路由时才会触发该守卫,跳转到其他路由时不会触发该守卫。

路由守卫的注册写在路由匹配规则数组里面:

let router = new Router({
routes: [
//...
{
path: '/users',
component: Users,
name: 'u',
beforeEnter: (to, from, next) => {
next(confirm('Entey Users?'));
}
}
]
});

示例:

10.3、组件守卫

组件守卫是针对单个组件进行监听,在访问到该组件时才会触发。

既然是组件守卫就写在组件里面,这里以进入Home组件为例:

export let Home = {
template: `
<div>
<h1>首页</h1>
<router-view></router-view>
<router-view name="b"></router-view>
</div>
`,
// 在路由跳转时,如果会访问到当前组件,则会触发该守卫
beforeRouteEnter(to, from, next) {
next(confirm('Enter Home?'));
},
// 在路由跳转时,如果离开当前组件,则会触发该守卫
beforeRouteLeave (to, from, next) {
next(confirm('Leave Home?'));
},
// 在当前路径下,当路由的参数发生变化时,才会触发该路由守卫
beforeRouteUpdate(to, from, next) {
console.log(to.params.path);
}
}

命名视图实现经典布局

什么是命名视图?

我们之前的 router-view 都是没有名字的,现在我们可以为其指定名称,就是命名视图。

我们要实现的目标是显示标题,侧边栏和主体三部分的经典视图,类似如下的布局:

那么既然要显示三部分,就需要三个 router-view,怎么让每个 router-view显示不同的内容呢?这就需要用到命名视图。

语法:

 <router-view name="view1"></router-view>

只需要指定一个name属性即可。

经典布局示例:

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
<!-- 1.引入vue-router文件 -->
<script src="./lib/vue-router-3.0.1.js"></script>
<style>
body,
html,
h1,
h2,
h3 {
margin: 0;
padding: 0;
}

.header {
width: 100%;
height: 80px;
background-color: red;
}

.left {
float: left;
width: 20%;
height: 500px;
background-color: green;
}

.right {
float: left;
width: 80%;
height: 500px;
background-color: blue;
}
</style>
</head>

<body>
<div id="box">
<router-view></router-view>
<router-view name="leftBanner"></router-view>
<router-view name="rightBanner"></router-view>
</div>

<script>
// 3、创建组件模板对象
var header = {
template: '<h1 class="header">顶部</h1>',
};

var left = {
template: '<h3 class="left">左边侧栏</h3>'
};

var right = {
template: '<h3 class="right">右边主体</h3>',
};

// 2、创建路由对象
var router = new VueRouter({
routes: [{
path: '/',
// component 要显示的组件为一个对象,default为默认显示的组件
// leftBanner表示router-view显示名称为leftBanner的组件
// rightBanner表示router-view显示名称为rightBanner的组件
components: {
'default': header,
'leftBanner': left,
'rightBanner': right
}
}]
});

var vm = new Vue({
el: "#box",
data: {},
methods: {},
// 4、将vm实例和路由对象联系起来
router
});
</script>
</body>

</html>

1、首先创建三个组件模板对象,分别用于显示头部,侧边栏,和主体三个部分。

2、在匹配规则里面, component 变成了 components,并且展示多个组件。其中:

default为默认显示的组件;

leftBanner表示router-view显示名称为leftBanner的组件

rightBanner表示router-view显示名称为rightBanner的组件。

3、为 router-view 指定 components 中显示不同组件的名称。

4、为显示的不同组件添加样式,形成经典布局样式。