什么是路由?
这里的路由并不是指我们平时所说的硬件路由器,这里的路由就是SPA(single page application)的路径管理器。vue的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来。
传统的页面应用,是用一些超链接来实现页面切换和跳转的。在vue-router单页面应用中,则是路径之间的切换,也就是组件的切换。路由模块的本质 就是建立起url和页面之间的映射关系。
至于我们为啥不能用a标签,这是因为用Vue做的都是单页应用(当你的项目准备打包时,运行npm run build时,就会生成dist文件夹,这里面只有静态资源和一个index.html页面),所以你写的标签是不起作用的,你必须使用vue-router来进行管理。
vue-router的实现原理之hash模式
单页面应用(SPA)的核心之一是: 更新视图而不重新请求页面;
vue-router在实现单页面前端路由时,提供了两种方式:Hash模式和History模式;根据mode参数来决定采用哪一种方式。
hash 模式的原理是 onhashchange 事件(监测hash值变化),可以在 window 对象上监听这个事件。
vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。hash(#)是URL 的锚点,代表的是网页中的一个位置,单单改变#后的部分,浏览器只会滚动到相应位置,不会重新加载网页,也就是说hash 出现在 URL 中,但不会被包含在 http 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面;
<div class='main'>
<a href="#md1">锚点1</a>
<a href="#md2">锚点2</a>
</div>
<div id="md1"></div>
<hr>
<div id="md2"></div>
以上代码解释了html页面中锚点的作用,即,当点击锚点2的a标签时,页面回跳至id为md2的div位置
路由简易实现
<body>
<div id='app'>
<!-- router-link 会被默认渲染成a标签,to属性会被渲染成href属性 -->
<router-link to='/liub'>刘邦</router-link>
<router-link to='/liuh'>刘恒</router-link>
<router-link to='/liuq'>刘启</router-link>
<router-view></router-view>
</div>
<script src='./js/vue.js'></script>
<script src='./js/vue-router_3.0.2.js'></script>
<script>
// 1. 定义(路由)组件.
// 因为是组件,所以当使用单文件组件时,可以从其他文件import进来
const lb = {
template: '<h3>汉高祖</h3>'
}
const lh = {
template: '<h3>汉文帝</h3>'
}
const lq = {
template: '<h3>汉景帝</h3>'
}
// 2.创建router实例,然后传入routes配置
const router = new VueRouter({
// 3. 定义路由
// 每个路由应该映射一个组件,其中'component'可以是通过extend创建的组件构造器
routes: [
{ path: '/liub', component: lb },
{ path: '/liuh', component: lh },
{ path: '/liuq', component: lq }
]
})
const app = new Vue({
el: '#app',
// 4.挂载到根实例
router,
data: {
}
})
</script>
</body>
router-link的标签自定义
<div id='app'>
<router-link to='/liub' tag='button'>刘邦</router-link>
<router-link to='/liuh'>刘恒</router-link>
<router-link to='/liuq'>刘启</router-link>
<router-view></router-view>
</div>
router-link默认渲染成a标签, tag属性, 可以指定渲染后的目标标签
const router = new VueRouter({
routes: [
{ path: '/liub', component: lb },
{ path: '/liuh', component: lh },
{ path: '/liuq', component: lq }
],
linkActiveClass: 'abc',
linkExactActiveClass: 'cde'
})
上述俩属性, 可以自定义被激活路由元素的class名称
路由重定向
routes: [
{ path: '/', redirect: '/liub' },
{ path: '/liub', component: lb },
{ path: '/liuh', component: lh },
{ path: '/liuq', component: lq }
],
当访问到根路径时自动跳转到/liub,注意是跳转,并不是默认显示/liub
routes: [
// redirect重定向
// { path: '/', component: lb },
// 写法1
// { path: '/', redirect: '/liub' },
// 写法2
// { path: '/', redirect: { path: '/liub' } },
{ path: '/', redirect: { name: 'lb' } },
// 写法3
// 方法接收 目标路由最为参数
// return 返回重定向的字符串
// { path: '/', redirect: to => { return '/liub' } },
{ path: '/liub', component: lb },
{ path: '/liuh', name: 'lb', component: lh },
{ path: '/liuq', component: lq }
],
路由重命名
“重定向”的意思是,当用户访问/时,URL 将会被替换成/liub,然后匹配路由为/liub,那么“别名”又是什么呢?
/demo 的别名是 /liub,意味着,当用户访问demo 时,URL 会保持为 /liub,但是路由匹配则为/demo,就像用户访问/demo 一样。
上面对应的路由配置为:
routes: [
{ path: '/demo', component: demo, alias: '/liub' },
{ path: '/liub', component: lb },
{ path: '/liuh', component: lh },
{ path: '/liuq', component: lq }
]
“别名”的功能让你可以自由地将UI 结构映射到任意的URL,而不是受限于配置的嵌套路由结构。
路由嵌套
当我们的一个组件里有子组件时,这个时候我们就需要使用嵌套路由访问到组件的子组件里面的内容
<body>
<div id='app'>
<router-link to='/liub' tag='button'>刘邦</router-link>
<router-link to='/liuh'>刘恒</router-link>
<router-link to='/liuq'>刘启</router-link>
<router-view></router-view>
</div>
<template id="lb">
<div>
<h3>汉高祖</h3>
<!-- 子路由连接 -->
<router-link to='/liub/liuy'>嫡长子刘盈</router-link>
<router-link to='/liub/liuf'>长子刘肥</router-link>
<!-- 子路由占位符 -->
<router-view></router-view>
</div>
</template>
<script src='./js/vue.js'></script>
<script src="./js/vue-router_3.0.2.js"></script>
<script>
const lb = {
template: '#lb'
}
const lh = {
template: '<h3>汉文帝</h3>'
}
const lq = {
template: '<h3>汉景帝</h3>'
}
const ly = {
template: '<h3>我是刘邦的嫡长子刘盈</h3>'
}
const lf = {
template: '<h3>我是刘邦的长子刘肥</h3>'
}
const router = new VueRouter({
routes: [
{
path: '/liub',
component: lb,
children: [
{ path: '/liub/liuy', component: ly },
{ path: '/liub/liuf', component: lf }
]
},
{ path: '/liuh', component: lh },
{ path: '/liuq', component: lq }
],
linkActiveClass: 'abc',
})
const app = new Vue({
el: '#app',
router,
data: {
}
})
</script>
</body>
动态路由匹配
<body>
<div id='app'>
<router-link to='/liub' tag='button'>刘邦</router-link>
<router-link to='/liuh/1'>刘恒1</router-link>
<router-link to='/liuh/2'>刘恒2</router-link>
<router-link to='/liuq'>刘启</router-link>
<router-view></router-view>
</div>
<template id="lb">
<div>
<h1>汉高祖</h1>
<!-- 子路由连接 -->
<router-link to='/liub/liuy'>嫡长子刘盈</router-link>
<router-link to='/liub/liuf'>长子刘肥</router-link>
<!-- 子路由占位符 -->
<router-view></router-view>
</div>
</template>
<script src='./js/vue.js'></script>
<script src='./js/vue-router_3.0.2.js'></script>
<script>
const lb = {
template: '#lb'
}
const lh = {
template: '<h3>我是刘恒的组件,我的id是:{{$route.params.id}}</h3>'
}
const lq = {
template: '<h3>汉景帝</h3>'
}
const ly = {
template: '<h3>我是刘邦嫡长子刘盈</h3>'
}
const lf = {
template: '<h3>我是刘邦长子刘肥</h3>'
}
const router = new VueRouter({
routes: [
{ path: '/', redirect: '/liub' },
{
path: '/liub',
component: lb,
children: [
{ path: '/liub/liuy', component: ly },
{ path: '/liub/liuf', component: lf }
]
},
{ path: '/liuh/:id', component: lh },
{ path: '/liuq', component: lq }
],
linkActiveClass: 'abc',
})
const app = new Vue({
el: '#app',
router,
data: {
}
})
</script>
</body>
路由props
<body>
<div id='app'>
<router-link to='/liub' tag='button'>刘邦</router-link>
<router-link to='/liuh/1'>刘恒1</router-link>
<router-link to='/liuh/2'>刘恒2</router-link>
<!-- <router-link :to="{name:'liuq'}">刘启</router-link> -->
<router-link :to="{name:'liuq', params:{id:5,name:'李四',age:500}}">刘启</router-link>
<router-view></router-view>
</div>
<template id="lb">
<div>
<h1>汉高祖</h1>
<!-- 子路由连接 -->
<router-link to='/liub/liuy'>嫡长子刘盈</router-link>
<router-link to='/liub/liuf'>长子刘肥</router-link>
<!-- 子路由占位符 -->
<router-view></router-view>
</div>
</template>
<script src='./js/vue.js'></script>
<script src='./js/vue-router_3.0.2.js'></script>
<script>
const lb = {
template: '#lb'
}
const lh = {
template: '<h3>我是刘恒的组件,我的id是:{{$route.params.id}}</h3>'
}
const lq = {
props: ['name', 'age'],
template: '<h3>汉景帝{{$route.params.id}}-->{{name}}-->{{age}}</h3>'
}
const ly = {
template: '<h3>我是刘邦嫡长子刘盈</h3>'
}
const lf = {
template: '<h3>我是刘邦长子刘肥</h3>'
}
const router = new VueRouter({
routes: [
{ path: '/', redirect: '/liub' },
{
path: '/liub',
component: lb,
children: [
{ path: '/liub/liuy', component: ly },
{ path: '/liub/liuf', component: lf }
]
},
{ path: '/liuh/:id', component: lh },
// { path: '/liuq/:id', name: 'liuq', component: lq, props: route => (console.log(route), { name: '张三', age: 20 }) }
{ path: '/liuq/:id', name: 'liuq', component: lq, props: route => (console.log(route), { name: route.params.name, age: route.params.age }) }
],
linkActiveClass: 'abc',
})
const app = new Vue({
el: '#app',
router,
data: {
}
})
</script>
</body>
编程式导航
上述都是使用一个声明出来的固定元素来实现跳转, 但这种方法局限性大, 必须要有某个固定的触发元素, 为了实现更加柔性的跳转, 我们就引申出编程式的导航
业务场景:在一个路由组件里面点击可以切换到另一个路由组件
this.$router.push(“目标路径”)// 强制跳转至某一路径
this.$router.go(-1)//返回上一级
this.$router.replace(“目标路径”)//路由替换
methods: {
go_lh() {
// push 直接跳转到某个组件页面
// console.log(this.$router.push('/liuh/1'));
this.$router.replace('/liuh/2')
}
}
......
methods: {
go_back() {
// 回退
this.$router.go(-1)
}
}
路由案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<style type="text/css">
html,
body,
#app {
margin: 0;
padding: 0px;
height: 100%;
}
.header {
height: 50px;
background-color: #131A20;
line-height: 50px;
text-align: center;
font-size: 24px;
color: #fff;
}
.footer {
height: 40px;
line-height: 40px;
background-color: #888;
position: absolute;
bottom: 0;
width: 100%;
text-align: center;
color: #fff;
}
.main {
display: flex;
position: absolute;
top: 50px;
bottom: 40px;
width: 100%;
}
.content {
flex: 1;
text-align: center;
height: 100%;
}
.left {
flex: 0 0 20%;
background-color: #1E1E1E;
}
.left a {
color: white;
text-decoration: none;
}
.right {
margin: 5px;
}
.btns {
width: 100%;
height: 35px;
line-height: 35px;
background-color: #f5f5f5;
text-align: left;
padding-left: 10px;
box-sizing: border-box;
}
button {
height: 30px;
background-color: #ecf5ff;
border: 1px solid lightskyblue;
font-size: 12px;
padding: 0 20px;
}
.main-content {
margin-top: 10px;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
ul li {
height: 45px;
line-height: 45px;
background-color: #545555;
color: #fff;
cursor: pointer;
border-bottom: 1px solid #fff;
}
table {
width: 100%;
border-collapse: collapse;
}
td,
th {
border: 1px solid #eee;
line-height: 35px;
font-size: 12px;
}
th {
background-color: #ddd;
}
</style>
<body>
<div id='app'>
<!-- 占位放app -->
<router-view></router-view>
</div>
<!-- 页面主组件 -->
<template id="web">
<div>
<!-- 头部区域 -->
<header class="header">享学后台管理系统</header>
<!-- 中间主体区域 -->
<div class="main">
<!-- 左侧菜单栏 -->
<div class="content left">
<ul>
<li>
<router-link to="/user">用户管理</router-link>
</li>
<li>
<router-link to="/rights">权限管理</router-link>
</li>
<li>
<router-link to="/goods">商品管理</router-link>
</li>
<li>
<router-link to="/orders">订单管理</router-link>
</li>
<li>
<router-link to="/settings">系统设置</router-link>
</li>
</ul>
</div>
<!-- 右侧内容区域 -->
<div class="content right">
<div class="main-content">
<router-view></router-view>
</div>
</div>
</div>
<!-- 尾部区域 -->
<footer class="footer">版权信息</footer>
</div>
</template>
<!-- user组件 -->
<template id="user">
<div>
<table>
<thead>
<tr>
<th>编号</th>
<th>姓名</th>
<th>年龄</th>
<th>操作1</th>
<th>操作2</th>
</tr>
</thead>
<tbody>
<tr v-for='(item,index) in userlist' :key='item.id'>
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.age}}</td>
<td>
<button @click='detail(item.id, item.det)'>详情</button>
</td>
<td>
<button @click='del(index)'>删除</button>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script src=' ./js/vue.js'></script>
<script src='./js/vue-router_3.0.2.js'></script>
<script>
const app = {
template: '#web'
}
const user = {
template: '#user',
data() {
return {
userlist: [
{ id: 1, name: '张三', age: 10, det: '我是张三我很帅' },
{ id: 2, name: '李四', age: 10, det: '我是李四我贼帅' },
{ id: 3, name: '王五', age: 10, det: '我是王五我无敌帅' },
]
}
},
methods: {
detail(id, det) {
this.$router.push(`/user_info/${id}${det}`)
},
del(index) {
this.userlist.splice(index, 1)
}
}
}
const rights = {
template: '<div><h3>权限管理区域</h3></div>'
}
const goods = {
template: '<div><h3>商品管理区域</h3></div>'
}
const orders = {
template: '<div><h3>订单管理区域</h3></div>'
}
const settings = {
template: '<div><h3>系统设置区域</h3></div>'
}
const user_info = {
props: ['id'],
template: '<div><h2>用户详情</h2><h3>用户信息:{{id}}</h3><button @click="go_back">回退</button></div>',
methods: {
go_back() {
this.$router.go(-1)
}
}
}
const router = new VueRouter({
routes: [
{
path: '/', redirect: '/user', component: app, children: [
{ path: '/user', component: user },
{ path: '/rights', component: rights },
{ path: '/goods', component: goods },
{ path: '/orders', component: orders },
{ path: '/settings', component: settings },
{ path: '/user_info/:id', component: user_info, props: true },
]
},
]
})
const vm = new Vue({
el: '#app',
router,
data: {
}
})
</script>
</body>
</html>