前端路由是直接找到与地址匹配的一个组件或对象并将其渲染出来。改变浏览器地址而不向服务器发出请求有两种做法,一是在地址中加入#以欺骗浏览器,地址的改变是由于正在进行页内导航;二是使用HTML5的window.history功能,使用URL的Hash来模拟一个完整的URL。将单页程序分割为各自功能合理的组件或者页面,路由起到了一个非常重要的作用。它就是连接单页程序中各页面之间的链条。
一、在vue中引入vue-router
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
二、路由配置
Vue.js是没有页面这个概念的,Vue.js的容器就只有组件。但我们用vue-router配合组件又会重新形成各种的"页面",我们可以这样来约定和理解:
- 页面是一个抽象的逻辑概念,用于划分功能场景
组件是页面在Vue的具体实现方式
vue-router提供了两个指令标签组件来处理这个导航与自动渲染逻辑:
- <router-view>——渲染路径匹配的视图组件,它还可以内嵌自己的<router-view>,根据嵌套路径渲染嵌套组件。
- <router-link>——支持用户再具有路由功能的应用中(点击)导航
例如,这里有三个router-link链接
const App = {
template: ·<ul>
<li>
<router-link to="/a">a</router-link>
<ul>
<li>
<router-link to="/a/aa">aa</router-link>
</li>
</ul>
</li>
</ul>·
}
const a = {
template: `<div>这里是a
// 这里是子路由aa显示的地方
<router-view></router-view>
</div>`
};
const aa = {
template: `<div>这里是a下面的子路由aa{{ $route.params }}</div>`
};
定义a路由及其子路由aa:
const router = new VueRouter({
routes: [
{
path: '/a',
component: a,
// 子路由不需要'/'
// 子路由显示的地方 父级路由模板里的第一个 router-view 标签里
children: [{
path: 'aa',
component: aa
}]
}]
})
vue-router提供了一种隐式的路由引用方式,vue-router将之称为"命名路由",简单点说就是通过路由的名称引用取代URL的直接引用。如下所示:
const router = new VueRouter({
routes: [
{name: 'Home', path: '/', component: Home},
{
name: 'Me', path: '/me', component: Me
}
]
})
在<router-link>内通过名称引用路由需要向to属性传入一个对象显式声明路由的名称:
<router-link :to="{name: 'Home'}">
使用命名路由引用时采用的是:to 而不是to, 因为这个时候向<router-link>传入的是一个对象{name:'Home'}而不是字符串。
三、输出指定元素
<router-link>组件支持用户在具有路由功能的应用中(点击)导航。通过to属性指定目标地址,默认渲染成带有正确链接的<a>标签。其实,我们并不需要输出<a>元素标记,因为我们并没有具体的链接地址,使用<li>元素同样可以处理来自用户的点击切换路由的事件。<router-link>可以通过配置tag属性生成别的标签,利用这个属性我们可以直接输出<li>而节省更多的代码:
<router-link :to="{ name: 'Home' }" tag="li"></router-link>
四、动态路由
将参数融入到路由的路径定义之内成为路径的一部分,是之更具有可读性,我们称这种参数为"动态路径参数",具体的做法是在参数名之前加上“:”,然后将参数写在路由的path内,具体定义如下:
routes: [
{
name: 'BookDetails',
// 这里的 :id 就是动态路径参数
path: '/books/:id',
component: BookDetails
}
]
在<router-link>中我们就可以加入一个params的属性来指定具体的参数值:
<router-link :to="{name: 'BookDetails', params: { id: 1}}">
</router-link>
在组件中获取这个id:
export default {
created () {
const bookID = this.$route.params.id
}
}
当使用路由参数时,例如从/books/1 导航到 /books/2,原来的组件实例会被复用。因为两个路由都渲染同一个组件,比起销毁再创建,复用显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用,也就是created、mounted等钩子函数再页面第二次加载时将失效。那么,当复用组件时,想对路由参数的变化做出响应的话,就需要再watch对象内添加对$route对象变化的跟踪函数:
export default {
template: '',
watch: {
'$route' (to, from) {
// 对路由变化作出响应
}
}
}
$route.params定义的参数必然是整个路由的其中一个部分,vue-router还可以让我们使用"/path?参数=值"的方式,也就是俗称的查询字符串传递数据。如果要从$route中读取Query string的参数,可以使用$route.query参数名的方式读取。
<div>
<router-link :to="{ path: '/b', query: { id: 123 }}">/b/bb</router-link>
</div>
const b = Vue.component('b', {
template: `<div>这里是b</div>`,
mounted () {
console.log(this.$route.query);
}
});
五、切页动效
使用Vue提供的<transition></transition>封装组件可以给任何元素和组件添加进入和退出的过渡效果。
有4个css类名在enter/leave的过渡中切换,以下是这4个类名的命名规则和作用。
- CSS类名-enter:定义进入过渡的开始状态。在元素被插入时生效,在下一帧移除。
- CSS类名-enter-active: 定义进入过渡的结束状态。在元素被插入时生效,在transition/animation完成之后移除。
- CSS类名-leave: 定义离开过渡的开始状态。在离开过度被触发时生效,在一个帧移除。
- CSS类名-leave-active: 定义离开过渡的结束状态。在离开过渡被触发时生效,在transition/animation完成之后移除。
<template>
<transition name="slide-fade">
<router-view></router-view>
</transition>
</template>
.slide-fade-enter-active {
transition: all .3s ease
}
.slide-fade-leave-active {
transition: all .3s cubic-bezier(1.0, 0.5, 0.8, 1.0);
}
.slide-fade-enter,
.slide-fade-leave-active {
transform: translateX(-430px)
opacity: 0
}
六、导航状态样式
在默认情况下当<router-link>对应的路由匹配成功时,就会自动设置class属性值为.router-link-active,如果我们想要将“激活”状态样式类命名为active,可以通过active-class属性进行设置,例如:
<router-link :to="{ name: 'Home' }" tag="li" active-class="active">
<div>
<img src="xxx">
</div>
</router-link>
如果在页面上都是这么显式声明,那么就需要在每个<router-link></router-link>组件元素上都要写一次;我们还可以有另外一个选择,就是在VueRouter的全局配置上进行声明,直接将.router-link-active这个默认值改为active,在main.js文件内的VueRouter配置中加入以下语句:
const router = new VueRouter({
linkActiveClass: "active"
});
通过linkActiveClass全局属性就能进行统一的设置了。
6.1、精确匹配与包含匹配
<router-link>添加“激活”状态样式类的默认依据是对URL地址的全包含匹配。举个例子,如果当前的路径是/home,那么<router-link to="/">也会被匹配并设置CSS类名。
想要链接使用“精确匹配模式”,则使用exact属性。在上面的例子中,“Home”路由就必须以精确匹配模式,否则它的tab被点击中之后,Home的tab会始终保持“激活”状态。
<!-- 这个链接只会在地址为/的时候被激活 -->
<router-link :to="{name: 'Home'}" exact>
七、History的控制
当我们使用HTML5的History模式的时候,每次路由的改变都会被"推"到导航历史中保留,在某些情况下我们不需要浏览器这样做,而是希望它能将原有的记录进行替换,那么我们据需要了解<router-link>是如何通过编程方式控制路由进行导航的。首先Vue实例内有一个$router对象,这个对象会提供三个方法,<router-link>则是用两种属性来对应这三个方法的调用:
router的方法 | 属性 | 说明 |
push() | - | 默认调用此方法 |
append() | append | 将目标URL追加到当前URL |
replace() | replace | 以目标URL替换现有的URL |
设置replace属性的话,当点击时,会调用router.replace()而不是router.push(),于是导航后不会留下History记录。
<router-link :to="{ name: 'Home'}" replace></router-link>
设置append属性后,则在当前(相对)路径前添加基路径。例如,我们从/a导航到一个相对路径b,如果没有配置append,则路径为/b,如果配置了,则为/a/b。
<router-link :to="{ path: 'relative/path'}" append></router-link>
八、关于Fallback
如果将路由配置为History模式,假如用户点击Home上的<router-link>时,浏览器的地址栏就会自动改变成对应的URL。如果我们直接在浏览器输入http://localhost/home,你会惊奇的发现浏览器会出现404的错误!
这是由于直接在浏览器输入http://localhost/home,浏览器就会直接将这个地址请求发送至服务器,先由服务器处理路由,而客户端路由的启动条件是要访问/index.html,所以,客户端路由完全失效了!
解决的办法是将所有发到服务器的请求利用服务端的URLRewrite模板重新转发给/index.html,启动VueRouter进行处理,而浏览器地址栏的URL保持不变。
当我们部署到生产环境时,就需要在web服务器进行一些简单配置以支持Fallback了。
Nginx
当出现404时将自动重定向至index.html
location / {
try_files $uri $uri/ /index.html;
}
本文内容摘抄自《vue2实践揭秘》