vue-router
一、 介绍
Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。包含的功能有:
- 嵌套的路由/视图表
- 模块化的、基于组件的路由配置
- 路由参数、查询、通配符
- 基于 Vue.js 过渡系统的视图过渡效果
- 细粒度的导航控制
- 带有自动激活的 CSS class 的链接
- HTML5 历史模式或 hash 模式,在 IE9 中自动降级
- 自定义的滚动条行为
二、 安装
1. NPM
npm install vue-router
如果在一个模块化工程中使用它,必须要通过 Vue.use() 明确地安装路由功能:
import Vue from "vue";
import VueRouter from "vue-router";
Vue.use(VueRouter);
如果使用全局的 script
标签,则无须手动安装。
2. CDN
<!-- 最新版 -->
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<!-- 指定版本 在@后加上版本号 -->
<script src="https://unpkg.com/vue-router@2.0.0/dist/vue-router.js"></script>
三、 vue-router 搭建
将 vue-router 添加到项目后只需要配置路由的结构就可以将 vue 文件映射到对应的路由中了。
<div id="app">
<h1>Hello App!</h1>
<p>
<!-- 使用 router-link 组件来导航. -->
<!-- 通过传入 `to` 属性指定链接. -->
<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
</div>
router.js
// 0. 如果使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter)
// 1. 定义 (路由) 组件。
// 可以从其他文件 import 进来
const Foo = { template: "<div>foo</div>" };
const Bar = { template: "<div>bar</div>" };
// 2. 定义路由
// 每个路由应该映射一个组件。 其中"component" 可以是
// 通过 Vue.extend() 创建的组件构造器,
// 或者,只是一个组件配置对象。
// 我们晚点再讨论嵌套路由。
const routes = [
{ path: "/foo", component: Foo },
{ path: "/bar", component: Bar }
];
// 3. 创建 router 实例,然后传 `routes` 配置
// 你还可以传别的配置参数, 不过先这么简单着吧。
const router = new VueRouter({
// (缩写) 相当于 routes: routes
routes
});
// 4. 创建和挂载根实例。
// 记得要通过 router 配置参数注入路由,
// 从而让整个应用都有路由功能
const app = new Vue({
router
}).$mount("#app");
// 现在,应用已经启动了!
四、 动态路由
我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果:
const User = {
template: "<div>User</div>"
};
const router = new VueRouter({
routes: [
// 动态路径参数 以冒号开头
{ path: "/user/:id", component: User }
]
});
带参跳转
this.$router.push({
name: "songlist",
params: {
id
}
});
五、 重定向和别名
1. 重定向
重定向也是通过 routes 配置来完成,下面例子是从 /a
重定向到 /b
:
const router = new VueRouter({
routes: [{ path: "/a", redirect: "/b" }]
});
重定向的目标也可以是一个命名的路由:
const router = new VueRouter({
routes: [{ path: "/a", redirect: { name: "foo" } }]
});
甚至是一个方法,动态返回重定向目标:
const router = new VueRouter({
routes: [
{
path: "/a",
redirect: to => {
// 方法接收 目标路由 作为参数
// return 重定向的 字符串路径/路径对象
}
}
]
});
注意导航守卫并没有应用在跳转路由上,而仅仅应用在其目标上。在下面这个例子中,为 /a
路由添加一个 beforeEach 或 beforeLeave 守卫并不会有任何效果。
2. 别名
“重定向”的意思是,当用户访问 /a
时,URL 将会被替换成 /b
,然后匹配路由为 /b
,那么“别名”又是什么呢?
/a
的别名是 /b
,意味着,当用户访问 /b
时,URL 会保持为 /b
,但是路由匹配则为 /a
,就像用户访问 /a
一样。
上面对应的路由配置为:
const router = new VueRouter({
routes: [{ path: "/a", component: A, alias: "/b" }]
});
“别名”的功能让你可以自由地将 UI 结构映射到任意的 URL,而不是受限于配置的嵌套路由结构。
六、 导航守卫
“导航”表示路由正在发生改变。 监听路由的变化
记住参数或查询的改变并不会触发进入/离开的导航守卫
1. 全局前置守卫
你可以使用 router.beforeEach 注册一个全局前置守卫:
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中。
每个守卫方法接收三个参数:
to: Route
: 即将要进入的目标 路由对象
from: Route
: 当前导航正要离开的路由
next: Function
: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next
方法的调用参数。
next()
: 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed
(确认的)。
next(false)
: 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from
路由对应的地址。
next('/')
或者 next({ path: '/' })
: 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: 'home' 之类的选项以及任何用在 router-link
的 to
prop 或 router.push
中的选项。
next(error)
: (2.4.0+) 如果传入 next
的参数是一个 Error
实例,则导航会被终止且该错误会被传递给 router.onError()
注册过的回调。
!> 确保要调用 next
方法,否则钩子就不会被 resolved
。
2. 全局解析守卫
在 2.5.0+ 你可以用 router.beforeResolve
注册一个全局守卫。这和 router.beforeEach
类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。
3. 全局后置钩子
你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next
函数也不会改变导航本身:
router.afterEach((to, from) => {
// ...
});
实例
import store from "../store";
import router from "../router";
// let topPath = ['/music','/recommend', '/rank','/singer']
let topPath = ["/music"];
router.afterEach((to, from) => {
// 判断当前是否一级路由, 是的话将启用侧边栏按钮
let use = true;
if (!topPath.includes(to.path)) {
use = false;
}
// 当都是一级路由时不触发 useSideBar ,减少开销
if (use != store.state.usesideBar) {
store.commit("useSideBar", use);
}
});
export default { router, store };
4. 路由独享的守卫
你可以在路由配置上直接定义 beforeEnter
守卫:
const router = new VueRouter({
routes: [
{
path: "/foo",
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
});
这些守卫与全局前置守卫的方法参数是一样的
七、 路由过渡动效
<router-view>
是基本的动态组件,所以我们可以用 <transition>
组件给它添加一些过渡效果:
<transition>
<router-view></router-view>
</transition>
1. 单个路由的过渡
上面的用法会给所有路由设置一样的过渡效果,如果你想让每个路由组件有各自的过渡效果,可以在各路由组件内使用 <transition>
并设置不同的 name
。
const Foo = {
template: `
<transition name="slide">
<div class="foo">...</div>
</transition>
`
}
const Bar = {
template: `
<transition name="fade">
<div class="bar">...</div>
</transition>
`
}
2. 基于路由的动态过渡
还可以基于当前路由与目标路由的变化关系,动态设置过渡效果:
<!-- 使用动态的 transition name -->
<transition :name="transitionName">
<router-view></router-view>
</transition>
// 接着在父组件内
// watch $route 决定使用哪种过渡
watch: {
'$route' (to, from) {
const toDepth = to.path.split('/').length
const fromDepth = from.path.split('/').length
this.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left'
}
}
八、 路由模式及原理
1. Hash模式
hash即浏览器url中#后面的内容,包含#。
- hash是URL中的锚点,代表的是网页中的一个位置,单单改变#后的部分,浏览器只会加载相应位置的内容,不会重新加载页面。
- 即#是用来指导浏览器动作的,对服务器端完全无用,HTTP请求中,不包含#。
- 每一次改变#后的部分,都会在浏览器的访问历史中增加一个记录,使用”后退”按钮,就可以回到上一个位置。
- 所以说Hash模式通过锚点值的改变,根据不同的值,渲染指定DOM位置的不同数据。
- 每次 hash 值的变化,会触发
hashchange
这个事件,通过这个事件我们就可以知道 hash 值发生了哪些变化。然后我们便可以监听hashchange
2. History模式
2014年后,因为HTML5标准发布。多了两个 API,pushState 和 replaceState,通过这两个 API 可以改变 url 地址且不会发送请求, 让开发人员在不刷新整个页面的情况下修改站点的URL。
当用户在浏览器点击进行后退、前进,或者在js中调用histroy.back(),history.go(),history.forward()等,会触发popstate事件;但pushState、replaceState不会触发这个事件。
3. 区别
1.直观区别:
- hash模式url带#号,history模式不带#号。
2.深层区别:
hash模式url里面永远带着#号,我们在开发当中默认使用这个模式。
如果用户考虑url的规范那么就需要使用history模式,因为history模式没有#号,是个正常的url适合推广宣传
功能也有区别,比如我们在开发app的时候有分享页面,那么这个分享出去的页面就是用vue或是react做的,
咱们把这个页面分享到第三方的app里,有的app里面url是不允许带有#号的,所以要将#号去除那么就要使用history模式
但是使用history模式还有一个问题就是,在访问二级页面的时候,做刷新操作,会出现404错误,那么就需要和后端人配合让他配置一下apache或是nginx的url重定向,重定向到你的首页路由上就ok啦。
路由模式配置:
export default new Router({
// mode: 'history',
mode: 'hash',
routes
})
如果是 history模式需要后端配合解决刷新404问题 这里以Node 后台为例:
const Koa = require('koa')
const Router = require('koa-router');
const static = require('koa-static')
const fs = require('fs');
const app = new Koa();
const router = new Router();
let str;
fs.readFile('../dist/index.html', "utf-8", (err, data) => {
if (err) {
ctx.body = "error found"
}
str = data.toString();
})
// 解决vue 路由在 history刷新 404情况
router.get('*', async(ctx, next) => {
if (ctx.url !== "/index.html") {
console.log("在这里返回")
ctx.body = str;
}
})
app.use(static("../dist/"));
app.use(router.routes()) //启动路由
app.use(router.allowedMethods());
app.listen(8989, () => {
console.log("监听服务器地址:127.0.0.1:8989");
})