【Vue】- Router

Vue Router

路由的使用步骤是:

  1. 引入VueVue Router
  2. 告诉Vue Router在哪里渲染(HTML)
  3. 把组件(components)映射到路由上
  4. vue实例上注入路由

一、基础路由

给同一个路径匹配多个路由时,先定义的路由优先级最高

<div id="app">
	<h1>Hello App</h1>
	<p>
		<!-- router-link标签会被渲染成a标签,to属性指向对应的路径 -->
		<!-- 活动的链接class属性会被设置为.router-link-active -->
		<router-link to="/foo">Go to Foo</router-link>
		<router-link to="/bar">Go to Bar</router-link>
		
		<!-- 给投传入一个对象以连接到对应的路由,相当于to:/user/999 -->
		<router-link :to="{name:'user',params:{id:999}}">Go to Bar</router-link>
	</p>
	<!-- router-view标签定义的是路由内容位置,路由匹配到的组件将渲染在这里 -->
	<router-view></router-view>
</div>
// 使用VueRouter构造函数创建一个router实例
const router = new VueRouter({
	// 开启history模式,不显示url中的hash值
	mode: 'history',
	
	// routes属性是一个路由数组,定义路由路径及对应组件
	routes: [{
			// 路径
			path: '/foo',
			// 对应组件,注意是component
			component: {
				template: `<div>foo</div>`
			}
		},{
			path: '/bar',
			component: {
				template: `<div>bar</div>`
			}
		},{
			// 给路由一个name属性作为命名路由,在<router-link>标签的to属性中传入一个对象,设置name以指向对应路由
			name: 'user',
			path: '/user:id',
			component: {
				template: `<div>user{{$route.params.id}}</div>`
			}
		}
	]
})

const app = new Vue({
	el: '#app',
	// 注入路由
	router
})

// 访问路由器
console.log(app.$router)
// 访问当前路由
console.log(app.$route)

二、动态路由

当访问的地址不同时也能访问到同一个内容(组件)
如:
localhost:80/user/99<user>组件
localhost:80/user/50<user>组件

  1. 动态路由配置
new Vue({
	el: '#app',
	router: new VueRouter({
		routes: [{
			// 动态路由的路径参数用冒号标记,该参数在跳转成功后会添加到$route.params中
			path: '/user/:id',
			component: {
				template: `<div>user{{$route.params.id}}</div>`
			}
		}, {
			// 一个路径上可以有多个路径参数,对应值都会设置到$route.params中
			path: '/user/:id/:name',
			component: {
				template: `<div>{{$route.params.id + $route.params.name}}</div>`
			}
		}]
	})
})
  1. 监听路由变化
new Vue({
	el: '#app',
	
	// 1. 用watch监听$route
	watch: {
		// to 和 from分别是目标和来源路径的路由对象
		'$route' (to,from) {
			console.log(to,from)
		}
	},
	// 2. 导航守卫
	beforeRouteUpdate (to, from, next) {
		// react to route changes...
		// don't forget to call next()
	}
})
  1. 通配符捕获所有路由
routes = [{
	// 匹配所有路径
	path: '*'
},{
	// 匹配所有/user-开头的路径
	path: '/user-*'
}]

三、嵌套路由

一个页面通常由多层嵌套的组件组合而成,如:

/user/foo/profile                     /user/foo/posts
+------------------+                  +-----------------+
| User             |                  | User            |
| +--------------+ |                  | +-------------+ |
| | Profile      | |  +------------>  | | Posts       | |
| |              | |                  | |             | |
| +--------------+ |                  | +-------------+ |
+------------------+                  +-----------------+

使用嵌套路由步骤如下:

  1. 在父级路径的模板上定义<router-view>标签
  2. 在父级路由上设置 children 属性
routes = [{
	// 父级路径,以/开头的嵌套路径会被当做根路径
	path: '/user',
	component: {
		// 在父级的模板中包含一个<router-view>标签作为路由出口
		template: 
		`<div class="user">
				<h2>User</h2><br>
				<router-view></router-view>
		</div>`
	},
	// 在父级路由上定义children属性,其值和router相同,可以嵌套多次children
	children: [{
		// 访问/user/profile时使用此组件
		path: 'profile',
		component: {
			template: `<div>profile</div>`
		}
	},{
		// 访问/user/posts时使用此组件
		path: 'posts',
		component: {
			template: `<div>posts</div>`
		}
	}]
}]

四、编程式导航

通过 $router 上的API来控制当前路由位置

  1. $router.push() 导航到指定的 url ,相当于点击, 此方法会在 history 栈中添加一个记录,点击浏览器后退按钮时回到之前的 url, 类似window.history.pushState(),参数如下:
params: ''; // 直接传url,不写/时为相对路径,添加到当前路径后
params: {
	// 有path时会忽略params
	path: '',
	
	// name和params配合,变成 user/999
	name: 'user',
	params: {
		userId: 999,
	},
	
	// 查询参数, 拼接到url后
	query: {}
}
  1. $router.replace() 作用同上,替换调当前的 history 记录,类似 window.history.replaceState()
  2. $router.go() 参数为整数,表示在 history 记录中前进或后退多少步,类似 window.history.go()

五、命名视图

在一个页面同时展示多个视图而不是嵌套展示时,可以在界面中定义多个 <router-view> 标签,没有设置 name 时默认为 default ,每一个视图都需要使用一个组件渲染

  1. 使用命名视图
<!-- 不写name时为default -->
<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>
routes: [
	{
		// 当在同一个路径时不同视图会被渲染为不同组件
		path: '/',
		
		// 注意是components
		components: {
			default: {
				template: '<div>is default</div>'
			},
			a: {
				template: '<div>is a</div>'
			},
			b: {
				template: '<div>is b</div>'
			}
		}
	}
]
  1. 在嵌套路由中使用命名视图
const userSettings = {
	template: `
	<div>
		<router-view/>
		<router-view name="helper"/>
	</div>
	`
}
routes: [{
	// 根路径下显示userSettings组件,该组件里有两个视图
	path: '/',
	component: userSettings,
	children: [{
		// 访问/emails时,渲染userSettings组件内的两个视图
		path: 'emails',
		components: {
			default: userProfileSub,
			helper: userEmailsSub
		}
	}]
}]

六、重定向和别名

  1. 重定向

访问 /a 时,url被替换为 /b,然后匹配路由为/b, 即访问 /a,实际跳转到 /b

routes: [{
	// 原地址
	path: '/notfound',
	
	// 重定向地址
	// 1.传入url
	redirect: '/',
	// 2.传入一个路由对象,name需要指向一个命名路由
	redirect: {name:'root'},
	// 3.传入一个方法,参数是目标路由,需要返回重定向目标(url或路由对象)
	redirect: to => '/'
	
	// 定义了重定向后component失效
	component: {
		template: '<div>no found</div>'
	}
}]
  1. 别名

定义一个路由 /a ,给 /a 设置一个别名 /b ,当访问/b时实际上匹配的是 /a 的路由

张三的外号叫狗蛋,别人叫张三是叫他,叫狗蛋也是叫他

routes: [{
	// 定义在根路径上的路由
	path: '/',
	component: A,
	
	// 给这个路由一个别名, 现在访问 /b 时会显示 A 组件
	alias: '/b'
}]

七、路由props传参

使用 props 代替 $route.params 访问属性来减少路由与 $route 的耦合

routes: [{
	// 动态路由,接收一个名为id的参数
	path: '/user/:id',
	
	// 1.只有一个视图时
	component: {
	// 在component中定义props来接收需要的参数,然后就能直接在模板中使用了
		props: ['id'],
		template: '<div>User{{id}}</div>'
	},
	// 一个视图是用一个值就行
	props: true,
	
	// 2.使用多个视图时
	// 多视图时需要分别为每个命名视图添加props
	components: {
		default: {
			props: ['id'],
			template: '<div>User{{id}}</div>'
		},
		vii: {
			props: ['id'],
			template: '<div>vii{{id}}</div>'
		}
	},
	// 多视图时props用对象分别定义
	props: {
		default: true,
		vii: false
	}
}]
  1. props设置为布尔值: 为 true 时, $route.params 会被设置为组件属性
  2. props设置为对象: 设置好参数的值,此后的动态路由参数值会被此参数值替代,即静态 props
// 单视图下
props: {
	id: 999
}

// 多命名视图下
props: {
	default: {
		id: 999
	},
	vii: {
		id: 888
	}
}
  1. props设置为函数: 创建一个函数,这样就能操作参数返回想要的值
// 单视图
routes: [{
	path: '/user/:id',
	component: {
		props: ['id'],
		template: '<div>User{{id}}</div>'
	},
	
	// 参数为路由对象
	props: (route) => {
		// 注意返回值不能为布尔值,而是一个对象,对象里需要定义要用到的属性
		return {
			id: route.params.id
		}
	}
}]


// 多视图
routes: [{
	path: '/user/:id',
	components: {
		default: {
			props: ['id'],
			template: '<div>User{{id}}</div>'
		},
		vii: {
			props: ['id'],
			template: '<div>vii{{id}}</div>'
		}
	},
	props: {
		// 多视图时需要给每一个命名视图添加对应函数
		default: (route) => {
			return {
				id: route.params.id
			}
		},
		vii: () => {
			return {
				id: 128
			}
		}
	}
}]

八、导航守卫

完整的导航解析流程: 
1.导航被触发。
2.在失活的组件里调用离开守卫。
3.调用全局的 beforeEach 守卫。
4.在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
5.在路由配置里调用 beforeEnter。
6.解析异步路由组件。
7.在被激活的组件里调用 beforeRouteEnter。
8.调用全局的 beforeResolve 守卫 (2.5+)。
9.导航被确认。
10.调用全局的 afterEach 钩子。
11.触发 DOM 更新。
12.用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。
  1. beforeEach 全局前置守卫

可以创建多个,按照创建顺序调用
守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中

const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
	// 必须调用next方法来resolve钩子
	next();

	// 中断当前导航
	next(false);

	// 跳转到指定地址(注意避免无限循环 --- 跳转后又被该守卫拦截到)
	next('/');

	// 传入一个Error实例是将终止导航并把错误传递给router.onError()注册过的回调
	next(error)
})
  1. beforeResolve 全局解析守卫

在导航被确认前,在所有组件内守卫异步路由组件被解析之后被调用

  1. afterEach 全局后置钩子

这样的钩子函数只能接受 tofrom, 不接受 next 函数, 也不会改变导航

  1. 局部守卫

设定给指定路由的导航守卫

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {}
    }
  ]
})
  1. 组件内守卫

在路由组件内直接定义导航守卫

const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`, 因为当守卫执行前,组件实例还没被创建
	// 可以通过传一个回调函数给next来访问组件实例, 这是唯一可以给next传递函数的守卫
	next(vm => {})
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用,可用于防止用户在未保存修改时突然离开
    // 可以访问组件实例 `this`
  }
}

九、路由元信息

在路由配置中定义 meta 属性,在守卫拦截导航时可以根据这个属性来做一些校验操作

在路由匹配中,每一次成功的匹配都会把这个路由记录添加到 $route.matched 数组中, 就能通过遍历这
个数组来访问 mata 属性

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      children: [
        {
			// 访问 /foo/bar 时,$route.matched中有两条记录,其中的第二条包含有meta属性
			path: 'bar',
			component: Bar,
			meta: { requiresAuth: true }
        }
      ]
    }
  ]
})

其他

  1. <router-view/> 可以被包裹在 <transition> 中实现过渡效果
  2. 数据获取有两种方法,用户体验不同
    获取数据后导航, 获取数据时用户会留在当前页面,此时可以展示进度条
    导航完成后获取数据, 获取数据时页面是空白的,此时可以展示loding
  3. router 可以控制 popstate 导航(浏览器的前进/后退按键触发)时的滚动行为,返回 {x:0,y:0} 来控制导航后的滚动条位置
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值