路由-前端路由
路由的概念其实是从后端来的。
- 根据某个访问的路径,进行逻辑处理,并返回 ------ 接口
前端的路由:一个路径,对应某个页面。
问题导入
在前面完成的资产管理案例中, 我们是把列表区域和添加表单区域实现在了一个页面中。当页面功能比较复杂时,我们需要它们拆分开来:一个页面中只显示一个区域。
一个比较直观的解决方案是把它们分别做成两个独立的网页文件,例如:
文件一: xxxx/index.html 显示表格区区域
文件二:xxxx/add.html显示表单区域
然后添加一个导航条来允许用户进行跳转。
这种解决方案比较直接了当,但它存在一些问题:
- 从一个页面跳入另一个页面需要重新加载公共的资源文件,造成浪费。例如index.html中需要用到axios.js,在add.html中也需要用到。
- 页面的跳入跳出给用户的体验也不好(特别地,是在移动端)。
那有没有一种方案在不进行页面跳转(不刷新整个页面,只存在一个.html页面)的前提下,能根据地址栏中的地址不同,来显示不同的内容?
有,这就是前端路由技术。
前端路由:
- 根据地址栏变化(不发请求),去局部更新不同的页面内容。
- 前端业务场景切换。
路由-实现原理
地址栏中的#有两个含义:
-
锚点链接: 在当前网页内部进行跳转。示例:https://baike.baidu.com/item/%E9%94%9A%E7%82%B9#3
-
哈希值(hash): 它的作用是,当它发生变化时,它不会导致当前页面的刷新值,但是它的变化会触发hashchange事件,这个事件能被监听到
路由原理
当hash变化时,更新内容,以达成显示不同内容的目标。
- hashchange: 在window上添加这个事件监听
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>
<a href="#/index">主页</a>
<a href="#/add">添加</a>
<a href="#/sql">搜索</a>
</div>
<div id="container">
<!-- 容器,根据自己约定的路由规则来显示不同的内容 -->
</div>
<script>
// 这里的index, add就是我们自己约定的路由规则:
// index ----> 主页
// add ----> 添加
// seq ----> 搜索
function hashChangeCallback () {
if(window.location.hash === '#/index') {
// 应该要显示主页
document.getElementById('container').innerHTML = `
我是一个主页,我这里有一个表格
`
} else if (window.location.hash === '#/add') {
// 应该要显示添加
document.getElementById('container').innerHTML = `
我是一个添加页,我这里有一个表单
`
} else if (window.location.hash === '#/sql') {
// 应该要显示添加
document.getElementById('container').innerHTML = `
搜索
`
}
}
window.addEventListener('DOMContentLoaded', () => {
// 页面加载完成
console.log('DOMContentLoaded')
hashChangeCallback()
})
window.addEventListener('hashchange', () => {
// 哈希值变化了
console.log('哈希值变化了', window.location)
hashChangeCallback()
})
</script>
</body>
</html>
技术要点:
- 地址url 中看到 #,这个 # 有两种情况,一个是我们所谓的锚点,比如典型的回到顶部按钮原理、Github 上各个标题之间的跳转等,路由里的 # 不叫锚点,我们称之为 hash。地址栏中hash的变化是不会发生页面跳转的。
- hashchange 事件用来监听hash值的变化。
- hash的改变也会记录到浏览历史中,通过回退和前进可以切换业务场景
SPA
单页面应用程序,简称SPA(single page application)一个系统上的所有功能在一个页面上实现,
(在移动端比较多,只要是用Vue开发的应用,绝大多数都是SPA)
SPA是通过前端路由实现业务场景的切换的。
在vue框架中额外引入vue-router插件来配合组件系统就可以轻易地实现。
在前端的三大框架中,都有成型的SPA的解决方案。
在单页面应用程序中,如何切换页面的业务场景。
优点:
- 整体不刷新页面,用户体验更好。
缺点:
- 首次加载会比较慢一点,后面也会讲优化的方式。
使用vue-router
vue-router是vue的一个插件,当我们的项目需要前端路由时,我们要先把它下载引入到页面中。
下载: https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js
导入插件
与vue的使用方法一样:
# 先下载到本地,再引用
<script src="./vue-router.min.js"></script>
# 直接引用
<script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script>
要先引入vue,后引入vue-router
初始化vue-router插件
用VueRouter构造器创建路由实例,并配置路由规则。
基本格式
const router = new VueRouter({
routes: [
{
path:"路径1",component:要展示的组件1},
{
path:"路径2",component:要展示的组件2},
.....
]
})
- path, component, routes 都是固定写法。
示例
- vue-router中有一套约定的规则用来确定在哪个url下显示哪个组件。
// 初始化vue-router且使用刚定义的路由规则
const router = new VueRouter({
// 初始化路由的配置对象
// 有以一个配置项 routes 定义路由规则
routes:[
{
path: '/', component: {
template:`<div>我是主页</div>`}},
{
path: '/news', component: {
template:`<div>新闻-生活早知道</div>`}},
{
path: '/sport', component: {
template:`<div>体育-体育改变人生</div>`} }
]
})
使用路由实例
在 Vue构造器中,有一项是router,它专门用来设置路由对象
new Vue({
el: '#app',
// vue提供了一个配置选项,router选项,是用来挂载路由实例的
// 只有挂载了 router 实例 才可使用路由的功能
router:路由对象
})
设置路由出口
在vue的模板,添加一个router-view组件,用它来指定当前路由对应组件渲染的位置。
<!-- 渲染路由对应的组件 router-view承载路由对应的组件的-->
<router-view></router-view>
测试使用
请直接在地址栏中补充对应的路由地址来查看路由效果。
http://127.0.0.1:5500/xxxxx.html#/news
完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script>
<style>
body{
background-color: #ccc;
}
#app{
width: 400px;
margin: 20px auto;
background-color: #fff;
border:4px solid blueviolet;
border-radius: 1em;
box-shadow: 3px 3px 3px rgba(0, 0, 0, .5);
padding:1em 2em 2em;
}
h3{
text-align: center;
}
.title{
display: flex;
justify-content: space-between;
align-items: center;
border:1px solid #ccc;
padding: 0 1em;
}
.title h4{
line-height: 2;margin:0;}
.container{
border:1px solid #ccc;
padding: 0 1em;
}
.btn {
/* 鼠标改成手的形状 */
cursor: pointer;
}
</style>
</head>
<body>
<div id="app">
<nav>
<ul>
<li><a href="#/index">主页</a></li>
<li><a href="#/add">添加</a></li>
<li><a href="#/seq">查询</a></li>
</ul>
</nav>
<!-- 路由出口
router-view是vue-router.js中提供的全局组件
用来装入 根据路由规则匹配成功的组件
-->
<router-view></router-view>
</div>
<script>
const seq = {
name: 'seq', template: '<div>我是搜索页</div>' }
// 1. 初始化vue-router: 用vueRouter去new一个路由对象
// VueRouter是在vue-router.js中提供的一个构造器
const router = new VueRouter({
// routes: 配置路由规则
routes: [
// { path: 路由地址1, component: 当地址栏访问路由地址1时,要显示的组件 }
// path, component都是固定写法
{
path: '/index', component: {
name: 'index', template: '<div>我是spa的主页,有什么可以帮助你的吗?</div>' } },
{
path: '/add', component: {
name: 'add', template: '<div>我是spa的添加页,要加个钟吗?</div>' } },
{
path: '/seq', component: seq },
]
})
// 2. 在vue实例中,使用router配置项,配置当前要使用的路由对象
const vm = new Vue({
el: "#app",
// router是一个配置项,固定写法。就和el, data, methods一样。
router: router
})
// 3. 在模板中设置路由出口 --- 添加一个容器,来用装入对应的组件
</script>
</body>
</html>
路由链接导航
通过router-link来进行路由跳转。
<!-- 写路由链接 不会使用a标签 使用router-link组件 -->
<nav>
<!-- 组件默认解析的是a标签 to属性跳转的地址,不需要带上# -->
<li>
<!-- router-link: vue-router提供的全局组件,有一个to属性,来进行跳转 -->
<router-link to="/index">主页</router-link>
</li>
<li>
<router-link to="/add">添加</router-link>
</li>
<li>
<router-link to="/seq">查询</router-link>
</li>
</nav>
- router-link组件会被vue解析成a标签,但不能直接通过a标签来跳转。
- 如果当前路由被激活会添加特殊的类名:
router-link-exact-active router-link-active
动态路由
目标:实现新闻详情的功能:即不同的新闻页使用同一个组件,根据传入的参数不同,达到显示不同内容的目标。
场景: 当你遇到详情页,就可以使用动态路由。
概念:不同的路由地址,指向同一个组件,此时需要使用动态路由。
示图:
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body{
background-color: #ccc;
}
#app{
width: 400px;
margin: 20px auto;
background-color: #fff;
border:4px solid blueviolet;
border-radius: 1em;
box-shadow: 3px 3px 3px rgba(0, 0, 0, .5);
padding:1em 2em 2em;
}
h3{
text-align: center;
}
.title{
display: flex;
justify-content: space-between;
align-items: center;
border:1px solid #ccc;
padding: 0 1em;
}
.title h4{
line-height: 2;margin:0;}
.container{
border:1px solid #ccc;
padding: 0 1em;
}
.btn {
/* 鼠标改成手的形状 */
cursor: pointer;
}
.router-link-active {
color: red;
font-size:30px;
}
</style>
</head>
<body>
<div id="app">
<nav>
<ul>
<li>
<!-- router-link: vue-router提供的全局组件,有一个to属性,来进行跳转 -->
<router-link to="/index">主页</router-link>
</li>
<li>
<router-link to="/add">添加</router-link>
</li>
<li>
<router-link to="/seq/18">查看18号</router-link>
<router-link to="/seq/28">查看28号</router-link>
</li>
</ul>
</nav>
<!-- 路由出口
router-view是vue-router.js中提供的全局组件
用来装入 根据路由规则匹配成功的组件
-->
<router-view></router-view>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script>
<script>
// 1. 初始化vue-router: 用vueRouter去new一个路由对象
// VueRouter是在vue-router.js中提供的一个构造器
const router = new VueRouter({
// routes: 配置路由规则
routes: [
// { path: 路由地址1, component: 当地址栏访问路由地址1时,要显示的组件 }
// path, component都是固定写法
{
path: '/index', component: {
name: 'index', template: '<div>我是spa的主页,有什么可以帮助你的吗?</div>' } },
{
path: '/add', component: {
name: 'add', template: '<div>我是spa的添加页,要加个钟吗?</div>' } },
// path: '/seq/:id':它会匹配所有 /seq/12 或者是 /seq/18 .... 这类地址
// 同时,12(18)会传给这里的id。理解:id是一个占位符。
// $route: 理解为一个全局变量:它在所有的组件中都可以直接访问。它其实是一个对象 ,是vueRouter.js添加到组件上的
// 一个对象,它里面保存着当前路由信息. $route.params中保存着动态路由传递的参数
{
path: '/seq/:id', component: {
name: 'seq', template: '<div>我是查询页 {
{$route.params.id}}</div>' } }
]
})
// 2. 在vue实例中,使用router配置项,配置当前要使用的路由对象
const vm = new Vue({
el: "#app",
// router是一个配置项,固定写法。就和el, data, methods一样。
router: router
})
// 3. 在模板中设置路由出口 --- 添加一个容器,来用装入对应的组件
</script>
</body>
</html>
总结:
- 在路由规则中,匹配到不同的地址,指向同一个组件
- 代码:
- 定义路由时,注意::id。
{path:'/seq/:id', component: seq}
- 定义路由时,注意::id。
- 数据:模板
{ {$route.params.id}}
组件this.$route.params.id
编程式导航
页面跳转(路由地址切换)有两种方式:
-
声明式导航。通过用户点击链接。router-link, to, 或者是 a href=’’。
它比较适合于固定不变化的导航栏。
-
编程式导航。通过this.$router.push(地址)跳转。
它比较适合于需要根据业务逻辑跳入不同页面的情况,例如,登陆功能。
格式
this.$router.push(地址)
$router: 是组件上的一个对象,在它的原型对象上面有很多的方法, push()用来做路由切换。
this.$router.push('/index')
应用场景:需要根据逻辑判断,跳入不同的页面,就可以使用编程式导航。例如:登陆功能。
示例代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body{
background-color: #ccc;
}
#app{
width: 400px;
margin: 20px auto;
background-color: #fff;
border:4px solid blueviolet;
border-radius: 1em;
box-shadow: 3px 3px 3px rgba(0, 0, 0, .5);
padding:1em 2em 2em;
}
h3{
text-align: center;
}
.title{
display: flex;
justify-content: space-between;
align-items: center;
border:1px solid #ccc;
padding: 0 1em;
}
.title h4{
line-height: 2;margin:0;}
.container{
border:1px solid #ccc;
padding: 0 1em;
}
.btn {
/* 鼠标改成手的形状 */
cursor: pointer;
}
.router-link-active {
color: red;
font-size:30px;
}
</style>
</head>
<body>
<div id="app">
<!-- 在模块写路由出口 -->
<router-view></router-view>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script>
<script>
const router = new VueRouter({
routes: [
// 路由规则
{
path: '/login',
component: {
name: 'login',
template: `
<div>登陆页
<button @click="hLogin">点击登陆</button>
</div>
`,
methods: {
hLogin () {
console.log(this.$router)
// 假设有50%的可能性
if (Math.random() > 0.5) {
// 跳入login.vue
// this.$router.push(地址)
// $router: 是组件上的一个对象,在它的原型对象上面有很多的方法, push()用来做路由切换。
this.$router.push('/index')
} else {
alert('失败')
}
}
}
}
},
{
path: '/index', component: {
name: 'index', template: `<div>我是主页</div>`}}
]
})
const vm = new Vue({
el: "#app",
router: router
})
</script>
</body>
</html>
路由-页面跳转及传参
目标
从页面(路由)pageA跳转到pageB(路由) ,并携带参数,在pageB中接收这个参数
跳转有两种方式
- 声明式:通过router-link的to属性跳转
- 编程式:通过$router.push()方法来跳转
传参有两种方式
- 查询传参
/pageB?id=1001
- 路径传参
/pageB/1001
获取传参数的方式: (根据传参不同,获取参数也不同)
- 查询传参: this.$route.query.id
- 路径传参: this.$route.params.id
示例代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body{
background-color: #ccc;
}
#app{
width: 400px;
margin: 20px auto;
background-color: #fff;
border:4px solid blueviolet;
border-radius: 1em;
box-shadow: 3px 3px 3px rgba(0, 0, 0, .5);
padding:1em 2em 2em;
}
h3{
text-align: center;
}
.title{
display: flex;
justify-content: space-between;
align-items: center;
border:1px solid #ccc;
padding: 0 1em;
}
.title h4{
line-height: 2;margin:0;}
.container{
border:1px solid #ccc;
padding: 0 1em;
}
.btn {
/* 鼠标改成手的形状 */
cursor: pointer;
}
.router-link-active {
color: red;
font-size:30px;
}
</style>
</head>
<body>
<div id="app">
<h2>目标:从pageA跳到pageB并带参数</h2>
<nav>
<ul>
<li>
<hr>
<h3>查询传参 - pageB?id=1&age=3</h3>
<!-- 1. 声明式跳转 并传参-查询传参-->
<router-link to="/pageB?id=1&age=3">声明式跳转并传参-查询传参</router-link>
<button @click="$router.push('/pageB?id=1&age=3')">编程式跳转并传参-查询传参</button>
</li>
<li>
<hr>
<h3>路径传参 - 动态路由pageA/1/3</h3>
<!-- 1. 声明式跳转 并传参-查询传参-->
<router-link to="/pageA/1/3">声明式跳转并传参-路径传参</router-link>
<button @click="$router.push('/pageA/1/3')">编程式跳转并传参-路径传参</button>
</li>
</ul>
</nav>
<!-- 路由出口
router-view是vue-router.js中提供的全局组件
用来装入 根据路由规则匹配成功的组件
-->
<router-view></router-view>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script>
<script>
const seq = {
name: 'seq', template: '<div>我是搜索页</div>' }
// 1. 初始化vue-router: 用vueRouter去new一个路由对象
// VueRouter是在vue-router.js中提供的一个构造器
const router = new VueRouter({
// routes: 配置路由规则
routes: [
{
path: '/pageB',
component:{
template: `
<div>
pageB, $route.query是一个对象, 用来接收通过查询传参的方式 传递的数据
pageB 从查询传参,{
{$route.query.id}} - {
{$route.query.age}}
</div>`
}
},
{
path: '/pageA/:id/:age',
component:{
template: `
<div>
pageA, $route.params是一个对象, 用来接收通过路径传参的方式 传递的数据
pageA 从查询传参,{
{$route.params.id}} - {
{$route.params.age}}
</div>`
}
}
]
})
// 2. 在vue实例中,使用router配置项,配置当前要使用的路由对象
const vm = new Vue({
el: "#app",
// router是一个配置项,固定写法。就和el, data, methods一样。
router: router
})
</script>
</body>
</html>
传参的不同,则获取参数的方式也不同。
pageA | pageB | |
---|---|---|
地址传参 | <router-link to="/pageB?id=1&age=3" | $route.query.id, $route.query.age |
路径传参 | <router-link to="/pageB/1/3" 。注意,在定义路由时,要把占位符提前写好:/pageB/:id/:age |
$route.params.id, $route.params.age |
路由-页面跳转及传参-复杂对象
$router.push()实参有两种格式:
-
字符串
this.$router.push('/pageA?id=1&age=3') this.$router.push('/pageA/1/3')
-
对象
// 对于query方法的传参,内容一定会在地址栏中出现 this.$router.push({ path: '/pageB', query:{ // query中的内容一定会在地址栏中出现 arr: [1, 2, 3, 4, 6], id: 1, age: 100 } }) // 如果是路由传参,则必须要使用name属性 this.$router.push({ name: 'pageA', // 路由的名字 params:{ // 这个arr在页面跳转时,会传到目标页,但是它并不会出现 // 地址栏中(因为在定义路由时,没有设置对应的占位符), arr: [1, 2, 3, 4, 6], id: 1, age: 100 } })
结论: 如果要传递复杂的数据,则必须使用$router.push({name, params: {}})这种格式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body{
background-color: #ccc;
}
#app{
width: 400px;
margin: 20px auto;
background-color: #fff;
border:4px solid blueviolet;
border-radius: 1em;
box-shadow: 3px 3px 3px rgba(0, 0, 0, .5);
padding:1em 2em 2em;
}
h3{
text-align: center;
}
.title{
display: flex;
justify-content: space-between;
align-items: center;
border:1px solid #ccc;
padding: 0 1em;
}
.title h4{
line-height: 2;margin:0;}
.container{
border:1px solid #ccc;
padding: 0 1em;
}
.btn {
/* 鼠标改成手的形状 */
cursor: pointer;
}
.router-link-active {
color: red;
font-size:30px;
}
</style>
</head>
<body>
<div id="app">
<h2>目标:从pageA跳到pageB并带参数</h2>
<button @click="hJumpQuery">编程式跳转并传参-查询传参</button>
<button @click="hJumpParams">编程式跳转并传参-路径传参</button>
<router-view></router-view>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script>
<script>
const seq = {
name: 'seq', template: '<div>我是搜索页</div>' }
// 1. 初始化vue-router: 用vueRouter去new一个路由对象
// VueRouter是在vue-router.js中提供的一个构造器
const router = new VueRouter({
// routes: 配置路由规则
routes: [
{
path: '/pageB',
component:{
template: `
<div>
pageB, $route.query是一个对象, 用来接收通过查询传参的方式 传递的数据
pageB 从查询传参,{
{$route.query.id}} - {
{$route.query.age}}
</div>`
}
},
{
name: 'pageA',
path: '/pageA/:id/:age',
// path: '/pageA',
component:{
template: `
<div>
pageA, $route.params是一个对象, 用来接收通过路径传参的方式 传递的数据
pageA 从查询传参,{
{$route.params.id}} - {
{$route.params.age}}
</div>`
}
}
]
})
// 2. 在vue实例中,使用router配置项,配置当前要使用的路由对象
const vm = new Vue({
el: "#app",
// router是一个配置项,固定写法。就和el, data, methods一样。
router: router,
methods: {
hJumpParams () {
// $router.push() 中的实参可以是字符串,也可以是对象
// this.$router.push('/pageA/1/100')
// this.$router.push({
// name: '路由的名字',
// params: {
// // 要传递的数据
// }
// })
this.$router.push({
name: 'pageA',
params:{
// 这个arr在页面跳转时,会传到目标页,但是它并不会出现
// 地址栏中(因为在定义路由时,没有设置对应的占位符),
arr: [1, 2, 3, 4, 6],
id: 1,
age: 100
}
})
},
hJumpQuery () {
this.$router.push({
path: '/pageB',
query:{
// query中的内容一定会在地址栏中出现
arr: [1, 2, 3, 4, 6],
id: 1,
age: 100
}
})
},
}
})
</script>
</body>
</html>
路由重定向
重定向:
- 当你访问某个地址的时候,经过程序的处理(用户看不见),跳转到了另外一个地址。
前端的路由,使用使用重定向功能,假设一个业务场景:
- 当你访问页面的时候,默认hash地址是
#/
,默认的路由地址/
- 此时我们项目的首页
/index
,所以:当我们访问/
重定向到/index
,才能默认访问首页。
代码:
const router = new VueRouter({
// routes: 配置路由规则
routes: [
// 重定向: 访问 A,给你跳到B
// path: '/': 这个路径是页面打开时,默认的地址。可以让它重定向到主页
{
path: '/', redirect: '/index' },
// { path: 路由地址1, component: 当地址栏访问路由地址1时,要显示的组件 }
// path, component都是固定写法
{
path: '/index', component: {
name: 'index', template: '<div>我是spa的主页,有什么可以帮助你的吗?</div>' } },
{
path: '/add', component: {
name: 'add', template: '<div>我是spa的添加页,要加个钟吗?</div>' } },
{
path: '/seq', component: seq }
]
})
总结:
- 路由规则对象中 提供了一个选项:redirect 配置重定向的地址即可。
路由-404页
404页:当我们访问一个不存在的页面时,给出一个友好的提示:让用户访问404页
格式:
{ path: '*', component: { template: '<div>你要找的页面,被外星人吃掉了~~~</div>' } }
思路:其它的路由规则都没有匹配成功,用它来保底。
const router = new VueRouter({
// routes: 配置路由规则
routes: [
// 重定向: 访问 A,给你跳到B
// path: '/': 这个路径是页面打开时,默认的地址。可以让它重定向到主页
{
path: '/', redirect: '/index' },
// { path: 路由地址1, component: 当地址栏访问路由地址1时,要显示的组件 }
// path, component都是固定写法
{
path: '/index', component: {
name: 'index', template: '<div>我是spa的主页,有什么可以帮助你的吗?</div>' } },
{
path: '/add', component: {
name: 'add', template: '<div>我是spa的添加页,要加个钟吗?</div>' } },
{
path: '/seq', component: seq },
// * 表示全部 , 实现404页
// 上面的规则都没有匹配成功,就到这里来
{
path: '*', component: {
template: '<div>你要找的页面,被外星人吃掉了~~~</div>' } }
]
})
案例- 改写 资产管理的例子
改写 资产管理的例子,用vue-router来改写。
核心步骤:
1- 写一个基本的路由结构(引入路由,定义路由规则,在vue实例上挂载,设置路由出口)
2- 完成对应的组件
- 添加功能,对应的组件
- 主页,对应的组件
示例代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/2.3.2/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div id="app">
<div class="container">
<nav>
<router-link to="/">主页</router-link>
<router-link to="/add">添加</router-link>
</nav>
<!-- 路由容器 -->
<router-view>
</router-view>
</div>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
// 定义一个组件
const pageAdd = {
template: `
<div>
<form class="form-inline">
<input type="text" v-model.trim="asset.name" class="form-control" placeholder="资产名称">
<input type="text" v-model.number="asset.price" class="form-control" placeholder="价格">
<button class="btn btn-primary" @click.prevent="hAdd">添加资产</button>
</form>
</div>
` ,
data () {
return {
asset: {
name: '',
price: ''
}
}
},
methods: {
// 用户点击了添加按钮
hAdd () {
console.log(this.asset)
// 1. 解构赋值
const {
name, price } = this.asset
// 2. if (用户的输入不合法,提示一下用户
if (name === '') {
alert('名字不能为空')
return
}
if (price <= 0) {
alert('价格不能是负数')
return
}
// 3. 发请求
axios({
method: 'POST',
url: 'http://localhost:3000/assets',
data:{
name: name,
price: price
}
}).then(res => {
console.log('添加成功')
// 2. 清空表单数据
this.asset.name = ''
this.asset.price = ''
// 3. 跳转到主页
this.$router.push('/')
})
}
}
}
const pageHome = {
template: `
<div>
<!-- 搜索 -->
<form class="form-inline" style="padding: 20px 0">
<input type="text"
v-model.trim="keyword"
class="form-control" placeholder="输入关键字进行搜索">
</form>
<!-- 表格 -->
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>编号</th>
<th>资产名称</th>
<th>价格</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, idx) in list">
<td>{
{idx}}</td>
<td>{
{item.name}}</td>
<td>{
{item.price}}</td>
<td><a href="#" @click.prevent="hDelete(item.id)">删除</a></td>
</tr>
<tr v-if="list.length==0">
<td colspan="4">没有数据</td>
</tr>
</tbody>
</table>
</div>`
,
data () {
return {
list: [
// {id:1,name:'商品1',price:1}
],
keyword: '', // 用来做搜索的关键字
}
},
created () {
console.log('created....')
this.loadData()
},
watch: {
keyword: function(newVal, oldVal) {
console.log(this.keyword)
// 获取到用户要搜索的关键字
// 如果关键字为空,就相当于是找全部的
axios({
method: 'GET',
url: 'http://localhost:3000/assets',
params: {
name_like: this.keyword
}
}).then(res => {
// 把查询到的数据更新到数据项中
this.list = res.data
})
}
},
methods: {
loadData () {
// 1. 发ajax请求,获取全部的数据
// 2. 把数据保存到data中的对应的数据项list中
axios({
method: 'GET',
url: 'http://localhost:3000/assets'
}).then(res => {
console.log(res.data)
// 数据变化了,视图也自动变化
this.list = res.data
})
},
hDelete (id) {
if(!window.confirm('你确定要删除吗?')) {
return
}
// alert(id)
// 调用接口,发请求
axios({
method: 'DELETE',
url: 'http://localhost:3000/assets/' + id
}).then(res => {
// 重新请求数据
this.loadData()
})
}
}
}
const router = new VueRouter({
routes: [
// 主页
{
path: '/', component: pageHome },
{
path: '/add',
component: pageAdd
}
]
})
new Vue({
el: '#app',
router: router
})
</script>
</html>
路由嵌套
原理:router-view中再次包含router-view。
背景:一个组件内部包含的业务还是很复杂,需要再次进行拆分。
格式:
routes:[
{
path: '/sport',
component: {
template:`<div><router-view></router-view></div>`},
children: [
{
path:'/xx1', component: {
}}
]
}
]
-
添加children
-
在组件的模块中,再次添加router-view
示例
总结:
- 在已有的路由容器中,再实现一套路由,再套一个路由容器,叫:嵌套路由。
代码:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>html页面</title>
<style>
body{
background-color: #eee;
}
#app{
background-color:#fff;
width: 500px;
margin: 50px auto;
box-shadow: 3px 3px 3px rgba(0 , 0, 0, 0.5);padding:2em;
}
.box{
padding: 1em;
border:1px solid #ccc;
margin:1em;
}
</style>
</head>
<body>
<div id="app">
<nav>
<router-link to="/">主页</router-link>
<router-link to="/news">新闻</router-link>
<router-link to="/sports">体育</router-link>
</nav>
<router-view></router-view>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>
<script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script>
<script>
const router = new VueRouter({
routes: [
{
path: '/', component: {
template: '<div>主页</div>'}},
{
path: '/news', component: {
template: '<div>我是新闻页</div>'}},
{
path: '/sports',
// 二级路由规则
// 这里的设置的组件,如果路由匹配成功,它们会显示在当前component中的router-view中。
children: [
{
path: '/sports/index', component: {
name: 'sportsIndex', template: `<div>-体育栏目的主页</div>`}},
{
path: '/sports/guowai', component: {
name: 'sportsGuowai', template: `<div>-国外体育新闻</div>`}},
{
path: '/sports/guonei', component: {
name: 'sportsGuonei', template: `<div>国内体育新闻</div>`}}
],
component: {
template: `
<div>
<h3>体育-体育改变人生</h3>
<nav>
<router-link to="/sports/index">主页</router-link>
<router-link to="/sports/guonei">国内</router-link>
<router-link to="/sports/guowai">国外</router-link>
</nav>
<router-view></router-view>
</div>`
}
}
]
})
var vm = new Vue({
el: '#app',
router: router
})
</script>
</body>
</html>
总结:
- 嵌套路由除了 router-view 之间需要嵌套,路由规则也需要通过children来实现嵌套。
如有不足,请多指教,
未完待续,持续更新!
大家一起进步!