路由简单使用
// App.vue
<div id="app">
<router-link class="tab-item" to="/myTrip">我的行程</router-link>
<router-link class="tab-item" to="/messageCenter">消息中心</router-link>
<router-link class="tab-item" to="/friendManage">好友管理</router-link>
<router-view></router-view>
</div>
// router/index.js
export default new Router({
routes: [
{
path: '/',
redirect: () => import('../views/MyTrip.vue')
}, {
path: '/myTrip',
component: () => import('../views/MyTrip.vue')
}, {
path: '/messageCenter',
component: () => import('../views/MessageCenter.vue')
}, {
path: '/friendManage',
component: () => import('../views/FriendManage.vue')
}
]
})
在app.vue中,
1.router-link
标签会默认渲染成一个a
标签,
2.to
属性是该路由指向的路径,
3. 如果希望不已a
标签来渲染,我们可以通过tag
属性来指定所需渲染的标签名
在index.js中,
1.routes
用于定义路由,
2.path
:为路由的路径,
3.component
:为当前这个路径所渲染的路由组件,
4.redirect
:为访问当前这个路径时,重定向的路由路径
编程式导航
编程式导航与声明式导航都可以实现路由的跳转。声明式导航是使用router-link
通过a
标签进行条船;而导航式,是通过click
事件绑定,调用router
上的方法进行跳转
router.push
,类似于window.history.pushState
// 参数传递字符串
this.$router.push('myTrip');
// 参数传递对象形式
this.router.push({path: 'myTrip'});
// name需要在路由上事前起好名字
this.router.push({name: 'trip', params:{id: '123'}});
// 带查询参数,变成/myTrip?id=234
this.router.push({path: 'myTrip', query:{id: '234'}});
router.replace
,类似于window.history.replaceState
router.replace
与router.push
很像,唯一一点不同就是,它不会向history
中添加新历史,而是与它的方法名一样——替换当前的history
记录
// 声明式
<router-link :to="..." replace>
// 导航式
this.$router.replace()
router.go
,类似于window.history.go
这个方法的参数是一个整数,意思是在history
记录中向前或者后退多少步
// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1)
// 后退一步记录,等同于 history.back()
router.go(-1)
// 前进 3 步记录
router.go(3)
// 如果 history 记录不够用,那就默默地失败呗
router.go(-100)
router.go(100)
路由参数
- 用
params
传参,F5
强制刷新参数会被清空(这里是通过name
进行的路由跳转,如果使用path
则无法获取到params
传递的参数)
// 路由配置
{
path: '/friendManage',
// 一定要写name,params必须用name来识别路径
name: 'friendManage',
component: () => import('../views/FriendManage.vue')
}
// 参数传递
this.$router.push({
name: `friendManage`,
params: {
user: 'Anna'
}
})
// 参数接受
this.$route.params.user
- 用
query
,由于参数适用路径传参的所以F5
强制刷新也不会被清空
// 路由配置
{
path: '/friendManage',
// 一定要写name,params必须用name来识别路径
name: 'friendManage',
component: () => import('../views/FriendManage.vue')
}
// 参数传递
this.$router.push({
name: `friendManage`,
// 也可以使用path来进行路由跳转 path: '/friendManage'
query: {
user: 'Anna'
}
})
// 参数接受
this.$route.query.user
嵌套路由
- 嵌套路由从字面上理解,就是当前的界面,是由多层嵌套的组件组合而成
router/index.js
// router/index.js嵌套路由配置
{ path: '/user/:id', component: User,
children: [
{
// 当 /user/:id/profile 匹配成功,
// UserProfile 会被渲染在 User 的 <router-view> 中
path: 'profile',
component: UserProfile
},
{
// 当 /user/:id/posts 匹配成功
// UserPosts 会被渲染在 User 的 <router-view> 中
path: 'posts',
component: UserPosts
}
]
}
要注意,以 / 开头的嵌套路径会被当作根路径。 这让你充分的使用嵌套组件而无须设置嵌套的路径
message-center.vue
<div class="user">
<h2>User {{ $route.params.id }}</h2>
<router-view></router-view>
</div>
hash与history模式的区别
vue-router
默认为hash
模式,使用URL
的hash
来模拟一个完整的URL
,于是当URL
改变时,页面不会重新加载- 如果用户考虑
url
的规范那么就需要使用history
模式,因为history
模式没有#
号,是个正常的url
适合推广宣传 - 但是使用
history
模式还有一个问题就是,在访问二级页面的时候,做刷新操作,会出现404
错误,就需要在服务端增加一个覆盖所有情况的候选资源:如果URL
匹配不到任何静态资源,则应该返回同一个index.html
页面,这个页面就是你app
依赖的页面
导航守卫
-
导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的 ,或者组件级的。
-
全局守卫:
router.beforeEach
,router.beforeResolve
, -
路由独享守卫:
beforeEnter
-
组件内守卫:
beforeRouteEnter
,beforeRouteUpdate
,beforeRouteLeave
-
router.beforeResolve
与router.beforeEach
类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用 -
完整的导航解析流程
- 导航被触发。
- 在失活的组件里调用
beforeRouteLeave
守卫。 - 调用全局的
beforeEach
守卫。 - 在重用的组件里调用
beforeRouteUpdate
守卫 (2.2+)。 - 在路由配置里调用
beforeEnter
。 - 解析异步路由组件。
- 在被激活的组件里调用
beforeRouteEnter
。 - 调用全局的
beforeResolve
守卫 (2.5+)。 - 导航被确认。
- 调用全局的
afterEach
钩子。 - 触发
DOM
更新。 - 调用
beforeRouteEnter
守卫中传给next
的回调函数,创建好的组件实例会作为回调函数的参数传入。
Vue-Router的简单实现
let _Vue = null;
export default class VueRouter{
// 静态方法不能被实例调用,只能通过类来调用。
static install(Vue){
// 1、判断当前插件是否已经被安装
if (VueRouter.install.installed){
return
}
VueRouter.install.installed = true;
// 2、把Vue构造函数记录到全局变量
_Vue = Vue;
// 3、把创建Vue实例的时候传入到router对象注入到Vue实例上
// 混入
_Vue.mixin({
beforeCreate(){
// 判断只要在router实例上才执行,如果是组件的话就不再执行
if (this.$options.router){
_Vue.prototype.$router = this.$options.router;
this.$options.router.init();
}
}
})
}
constructor(options){
this.options = options;
this.routeMap = {}; // 解析路由规则
this.data = _Vue.observable({ // 创建响应式对象
current: '/'
})
}
init(){
this.initRouteMap();
this.initComponents(_Vue);
this.initEvent();
}
// 遍历所有的路由规则,把路由规则解析成键值对的形式,存储到routeMap中
initRouteMap(){
this.options.routes.forEach(route => {
this.routeMap[route.path] = route.component;
})
}
initComponents(Vue){
const self = this;
Vue.component('router-link', {
props: {
to: String
},
// template: `<a :href="to"><slot></slot></a>`
render(h){
return h('a', {
attrs: {
href: this.to
},
on: {
click: this.clickHandler
}
}, [this.$slots.default])
},
methods: {
clickHandler(e){
history.pushState({}, '', this.to);
this.$router.data.current = this.to;
e.preventDefault();
}
}
})
Vue.component('router-view', {
render(h){
const component = self.routeMap[self.data.current];
return h(component);
}
})
}
initEvent(){
window.addEventListener('popstate', () => {
this.data.current = window.location.pathname;
})
}
}