# 第八模块:前端VUE2-组件化开发
# 3.组件化开发
在开发过程中,我们可以将页面中 某一部分 的功能编写成一个组件,然后再在页面上进行引用。
- 有利于划分功能模块的开发(HTML、CSS、JavaScript等相关代码都集成到组件中)。
- 有利于重用
# 3.1 局部组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1>=======当前页面=======</h1>
{{name}}
<h1>=======引入子组件=======</h1>
<Demo></Demo>
<Demo></Demo>
<Bb></Bb>
<Bb></Bb>
<hr/>
<Bb></Bb>
</div>
<script>
//创建子组件
const Demo = {
data:function() {
return {
msg: '哈哈哈哈哈'
}
},
template: `
<div>
<h1>{{msg}}</h1>
<input type="text" v-model="msg"/>
<input type="button" @click="showMeg" value="点我呀">
</div>
`,
methods: {
showMeg: function () {
alert(this.msg);
}
}
}
//创建子组件
const Bili = {
// 组件中的data是一个方法,并返回值(与Vue对象创建不同)
data:function() {
return {
dataList: [
{"id": 1, "title": "2022再见"},
{"id": 2, "title": "2023你好"},
]
}
},
template: `
<div>
<h1>数据列表</h1>
<table border="1">
<thead>
<tr>
<th>ID</th>
<th>标题</th>
</tr>
</thead>
<tbody>
<tr v-for="item in dataList">
<td>{{item.id}}</td>
<td>{{item.title}}</td>
</tr>
</tbody>
</table>
</div>
`
}
var app = new Vue({
el: '#app',
data: {
name: "linda",
},
components: {
Demo,
Bb: Bili
},
methods: {}
})
</script>
</body>
</html>
# 3.2 全局组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1>=======当前页面=======</h1>
{{name}}
<h1>=======引入子组件=======</h1>
<Demo></Demo>
<Demo></Demo>
<Bili></Bili>
<Bili></Bili>
</div>
<script>
//创建子组件
Vue.component('Demo', {
data: function () {
return {
msg: '哈哈哈哈哈'
}
},
template: `
<div>
<h1>{{msg}}</h1>
<input type="text" v-model="msg"/>
<input type="button" @click="showMeg" value="点我呀">
</div>
`,
methods: {
showMeg: function () {
alert(this.msg);
}
}
});
//创建子组件
Vue.component('Bili', {
// 组件中的data是一个方法,并返回值(与Vue对象创建不同)
data: function () {
return {
dataList: [
{"id": 1, "title": "2022再见"},
{"id": 2, "title": "2023你好"},
]
}
},
template: `
<div>
<h1>数据列表</h1>
<table border="1">
<thead>
<tr>
<th>ID</th>
<th>标题</th>
</tr>
</thead>
<tbody>
<tr v-for="item in dataList">
<td>{{item.id}}</td>
<td>{{item.title}}</td>
</tr>
</tbody>
</table>
</div>
`
});
var app = new Vue({
el: '#app',
data: {
name: "linda",
},
methods: {}
})
</script>
</body>
</html>
# 4.vue-router组件
vue + vue-router组件 可以实现 SPA(single Page Application),即:单页面应用。
单页面应用,简而言之就是项目只有一个页面。
一个页面如何呈现多种界面的效果呢?
- 基于vue开发多个组件,例如:活动组件、课程组件、咨询组件
- 在页面上 vue-router 用来管理这些组件,用户点击某个按钮,就显示特定的组件(数据基于Ajax获取)。
# 4.1 下载和引用
官方地址:https://router.vuejs.org/zh/
下载地址:https://unpkg.com/vue-router@3
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- vue-router.js 依赖 vue.js -->
<script src="vue.js"></script>
<script src="vue-router.js"></script>
</head>
<body>
...
</body>
</html>
注意:后期用脚手架开发时,可以直接使用npm下载和引用。
# 4.2 快速上手
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>day03v1</title>
<style>
body {
margin: 0;
}
.container {
width: 980px;
margin: 0 auto;
}
.menu {
height: 48px;
background-color: #499ef3;
line-height: 48px;
}
.menu a {
color: white;
text-decoration: none;
padding: 0 10px;
}
</style>
<script src="vue.js"></script>
<script src="vue-router.js"></script>
</head>
<body>
<div id="app">
<div class="menu">
<div class="container">
<router-link to="/">Logo</router-link>
<router-link to="/home">首页</router-link>
<router-link to="/course">课程</router-link>
<router-link to="/news">咨询</router-link>
</div>
<div class="container">
<router-view></router-view>
</div>
</div>
</div>
<script>
const Home = {template:'<div><h1>首页内容</h1></div>'}
const Course = {template:'<div><h1>课程内容</h1></div>'}
const News = {template:'<div><h1>咨询内容</h1></div>'}
const router = new VueRouter({
routes: [
{
path: "/",
component: Home,
},
{
path: "/home",
component: Home,
},
{
path: "/course",
component: Course,
},
{
path: "/news",
component: News,
}
]
})
var app = new Vue({
el: "#app",
data:{
},
router,
});
</script>
</body>
</html>
# 案例:开发学城(第1版)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>开发学城</title>
<style>
body {
margin: 0;
}
.container {
width: 1100px;
margin: 0 auto;
}
.menu {
height: 48px;
background-color: #499ef3;
line-height: 48px;
}
.menu a {
color: white;
text-decoration: none;
padding: 0 10px;
}
.course-list {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
}
.course-list .item {
width: 248px;
padding: 10px;
border: 1px solid #dddddd;
margin-right: 5px;
margin-top: 10px;
}
.course-list .item img {
width: 100%;
height: 120px;
}
</style>
<script src="vue.js"></script>
<script src="vue-router.js"></script>
</head>
<body>
<div id="app">
<div class="menu">
<div class="container">
<router-link to="/">开发学城</router-link>
<router-link to="/home">首页</router-link>
<router-link to="/course">课程</router-link>
<router-link to="/news">咨询</router-link>
</div>
</div>
<div class="container">
<router-view></router-view>
</div>
</div>
<script>
const Home = {
data: function () {
return {
title: "欢迎使用开发学城"
}
},
template: `<h2>{{title}}</h2>`
}
const Course = {
data: function(){
return {
courseList: []
}
},
created: function(){
/* 组件创建完成之后自动触发【此时组件的对象已创建,但还未将页面相关的DOM创建并显示在页面上】
- 可以去操作组件对象,例如:this.courseList = [11,22,33]
- 不可以去操作DOM,例如:document.getElementById (未创建)
实际会用 axios({请求数据})
*/
this.courseList = [
{"name": "Golang从入门到精通", "cover": "http://127.0.0.1:5500/src/img/go.png"},
{"name": "Python从入门到精通", "cover": "http://127.0.0.1:5500/src/img/py.png"},
{"name": "DBA进阶之旅之MySQL", "cover": "http://127.0.0.1:5500/src/img/mysql.png"},
{"name": "DBA进阶之旅之Redis", "cover": "http://127.0.0.1:5500/src/img/redis.png"},
]
},
mounted: function () {
/* DOM对象已在页面上生成,此时就可以 */
},
template: `
<div class="course-list">
<div class="item" v-for="item in courseList">
<img :src="item.cover" alt="">
<a>{{item.name}}</a>
</div>
</div>
`
}
const News = {template:'<div><h2>咨询内容</h2></div>'}
const router = new VueRouter({
routes: [
{
path: "/",
component: Home,
},
{
path: "/home",
component: Home,
},
{
path: "/course",
component: Course,
},
{
path: "/news",
component: News,
}
]
})
var app = new Vue({
el: "#app",
data:{
},
router,
});
</script>
</body>
</html>
# 4.3 路由和传值
当某个组件可以根据某些参数值的不同,展示不同效果时,需要用到动态路由。
例如:访问网站看到课程列表,点击某个课程,就可以跳转到课程详细页面(根据课程ID不同展示不同数据)。
如何来设置动态路由呢?
定义路由
const router = new VueRouter({ routes: [ { path: '/', component: Home}, { path: '/course', component: Course, name: "Course"} { path: '/detail/:id', component: Detail, name: "Detail"} ], })
HTML展示
<div> <router-link to="/">首页</router-link> <router-link to="/course">课程</router-link> <router-link to="/detail/123">课程</router-link> <router-link :to="{path:'/course'}">课程</router-link> <router-link :to="{path:'/course?size=19&page=2'}">课程</router-link> <router-link :to="{path:'/course', query:{size:19,page:2}">课程</router-link> <router-link :to="{name:'Course'}">课程</router-link> <router-link :to="{name:'Course', query:{size:19,page:2} }">课程</router-link> <router-link :to="{path:'/detail/22',query:{size:123}}">Linux</router-link> <router-link :to="{name:'Detail',params:{id:3}, query:{size:29}}">网络安全</router-link> </div> <h1>内容区域</h1> <router-view></router-view>
组件获取URL传值和GET参数
const Detail = { data: function () { return { title: "详细页面", paramDict: null, queryDict: null, } }, created: function () { this.paramDict = this.$route.params; this.queryDict = this.$route.query; // 发送axios请求 }, template: `<div><h2>{{title}}</h2><div>当前请求的数据 {{paramDict}} {{queryDict}}</div></div>` }
# 案例:开发学城(第2版)
点击课程,查看课程详细页面。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>开发学城02</title>
<style>
body {
margin: 0;
}
.container {
width: 1100px;
margin: 0 auto;
}
.menu {
height: 48px;
background-color: #499ef3;
line-height: 48px;
}
.menu a {
color: white;
text-decoration: none;
padding: 0 10px;
}
.course-list {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
}
.course-list .item {
width: 248px;
padding: 10px;
border: 1px solid #dddddd;
margin-right: 5px;
margin-top: 10px;
}
.course-list .item img {
width: 100%;
height: 120px;
}
</style>
<script src="vue.js"></script>
<script src="vue-router.js"></script>
</head>
<body>
<div id="app">
<div class="menu">
<div class="container">
<router-link to="/">开发学城</router-link>
<router-link to="/home">首页</router-link>
<router-link to="/course">课程</router-link>
<router-link to="/news">咨询</router-link>
</div>
</div>
<div class="container">
<router-view></router-view>
</div>
</div>
<script>
const Home = {
data: function () {
return {
title: "欢迎使用开发学城"
}
},
template: `<h2>{{title}}</h2>`
}
const Course = {
data: function(){
return {
courseList: []
}
},
created: function(){
/* 组件创建完成之后自动触发【此时组件的对象已创建,但还未将页面相关的DOM创建并显示在页面上】
- 可以去操作组件对象,例如:this.courseList = [11,22,33]
- 不可以去操作DOM,例如:document.getElementById (未创建)
实际会用 axios({请求数据})
*/
this.courseList = [
{id: 111, name: "Golang从入门到精通", cover: "http://127.0.0.1:5500/src/img/go.png"},
{id: 222, name: "Python从入门到精通", cover: "http://127.0.0.1:5500/src/img/py.png"},
{id: 333, name: "DBA进阶之旅之MySQL", cover: "http://127.0.0.1:5500/src/img/mysql.png"},
{id: 444, name: "DBA进阶之旅之Redis", cover: "http://127.0.0.1:5500/src/img/redis.png"},
]
},
mounted: function () {
/* DOM对象已在页面上生成,此时就可以 */
},
template: `
<div class="course-list">
<div class="item" v-for="item in courseList">
<router-link :to="{name: 'Detail', params: {id: item.id}}">
<img :src="item.cover" alt="">
</router-link>
<a>{{item.name}}</a>
</div>
</div>
`
}
const Detail = {
// data: function(){
// return {
// title: "详细页面",
// paramDict: null,
// queryDict: null,
// }
// },
// created: function () {
// this.paramDict = this.$route.params;
// this.queryDict = this.$route.query;
// // 发送axios请求
// },
data: function(){
return {
title: "详细页面",
courseId: null
}
},
created: function () {
this.courseId = this.$route.params.id;
// 此处可以根据课程ID,发送ajax请求获取课程详细信息
},
template: `<div><h2>课程详细页面</h2><div>当前课程ID为:{{courseId}}</div></div>`
}
const News = {template:'<div><h2>咨询内容</h2></div>'}
const router = new VueRouter({
routes: [
{
path: "/",
component: Home,
},
{
path: "/home",
component: Home,
},
{
path: "/course",
component: Course,
name: "Course",
},
{
path: "/detail/:id",
component: Detail,
name: "Detail",
},
{
path: "/news",
component: News,
}
]
})
var app = new Vue({
el: "#app",
data:{
},
router,
});
</script>
</body>
</html>
# 4.5 无法刷新
上述编写案例是没有问题,但如果在开发中会涉及到 同一个路由的跳转(默认不会重新加载页面,数据无法获取)。
例如:在详细页面再出现一个课程推荐,即:在课程详细,点击推荐的课程后跳转到课程详细页面(课程ID不同),此时课程的ID还是原来加载的ID,无法获取推荐课程的ID。
如何解决呢?
在课程详细的组件中设置watch属性即可,watch会监测$route
值,一旦发生变化,就执行相应的函数。
const Detail = {
data: function () {
return {
title: "详细页面",
courseId: null,
}
},
created: function () {
this.courseId = this.$route.params.id;
this.getCourseDetail();
},
watch: {
$route:function(to, from) {
this.courseId = to.params.id;
// this.getCourseDetail();
}
},
methods: {
getCourseDetail: function () {
// 根据this.courseId获取课程详细信息
}
},
template: `<div><h2>{{title}}</h2><div>当前请求的数据 {{paramDict}} {{queryDict}}</div></div>`
}
# 案例:开发学城(第3版)
在详细页面实现推荐课程
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>开发学城03</title>
<style>
body {
margin: 0;
}
.container {
width: 1100px;
margin: 0 auto;
}
.menu {
height: 48px;
background-color: #499ef3;
line-height: 48px;
}
.menu a {
color: white;
text-decoration: none;
padding: 0 10px;
}
.course-list {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
}
.course-list .item {
width: 248px;
padding: 10px;
border: 1px solid #dddddd;
margin-right: 5px;
margin-top: 10px;
}
.course-list .item img {
width: 100%;
height: 120px;
}
</style>
<script src="vue.js"></script>
<script src="vue-router.js"></script>
</head>
<body>
<div id="app">
<div class="menu">
<div class="container">
<router-link to="/">开发学城</router-link>
<router-link to="/home">首页</router-link>
<router-link to="/course">课程</router-link>
<router-link to="/news">咨询</router-link>
</div>
</div>
<div class="container">
<router-view></router-view>
</div>
</div>
<script>
const Home = {
data: function () {
return {
title: "欢迎使用开发学城"
}
},
template: `<h2>{{title}}</h2>`
}
const Course = {
data: function(){
return {
courseList: []
}
},
created: function(){
/* 组件创建完成之后自动触发【此时组件的对象已创建,但还未将页面相关的DOM创建并显示在页面上】
- 可以去操作组件对象,例如:this.courseList = [11,22,33]
- 不可以去操作DOM,例如:document.getElementById (未创建)
实际会用 axios({请求数据})
*/
this.courseList = [
{id: 111, name: "Golang从入门到精通", cover: "http://127.0.0.1:5500/src/img/go.png"},
{id: 222, name: "Python从入门到精通", cover: "http://127.0.0.1:5500/src/img/py.png"},
{id: 333, name: "DBA进阶之旅之MySQL", cover: "http://127.0.0.1:5500/src/img/mysql.png"},
{id: 444, name: "DBA进阶之旅之Redis", cover: "http://127.0.0.1:5500/src/img/redis.png"},
]
},
mounted: function () {
/* DOM对象已在页面上生成,此时就可以 */
},
template: `
<div class="course-list">
<div class="item" v-for="item in courseList">
<router-link :to="{name: 'Detail', params: {id: item.id}}">
<img :src="item.cover" alt="">
</router-link>
<a>{{item.name}}</a>
</div>
</div>
`
}
const Detail = {
// data: function(){
// return {
// title: "详细页面",
// paramDict: null,
// queryDict: null,
// }
// },
// created: function () {
// this.paramDict = this.$route.params;
// this.queryDict = this.$route.query;
// // 发送axios请求
// },
data: function(){
return {
title: "详细页面",
courseId: null,
hotCourseList: [
{id: 333, title: "DBA进阶之旅之MySQL"},
{id: 444, title: "DBA进阶之旅之Redis"},
],
}
},
created: function () {
this.courseId = this.$route.params.id;
// 此处可以根据课程ID,发送ajax请求获取课程详细信息
this.getCourseDetail();
},
watch: {
$route:function(to, from) {
this.courseId = to.params.id;
// this.getCourseDetail();
}
},
methods: {
getCourseDetail: function () {
// 根据this.courseId获取课程详细信息
}
},
template:`
<div>
<h2>课程详细页面</h2>
<div>当前课程ID为:{{courseId}}</div>
<h3>课程推荐</h3>
<ul>
<li v-for="item in hotCourseList">
<router-link :to="{name:'Detail', params:{id:item.id}}">{{item.title}}</router-link>
</li>
</ul>
</div>`
}
const News = {template:'<div><h2>咨询内容</h2></div>'}
const router = new VueRouter({
routes: [
{
path: "/",
component: Home,
},
{
path: "/home",
component: Home,
},
{
path: "/course",
component: Course,
name: "Course",
},
{
path: "/detail/:id",
component: Detail,
name: "Detail",
},
{
path: "/news",
component: News,
}
]
})
var app = new Vue({
el: "#app",
data:{
},
router,
});
</script>
</body>
</html>
# 4.6 路由嵌套
const router = new VueRouter({
routes: [
{
path: '/pins/',
component: Pins,
children: [
{
// 当 /pins/hot 匹配成功,
// Hot组件 会被渲染在 Pins 的 <router-view> 中
path: 'hot',
component: Hot
},
{
// 当 /pins/following 匹配成功,
// Following组件 会被渲染在 Pins 的 <router-view> 中
path: 'following',
component: Following
}
]
}
]
})
# 案例:开发学城(第4版)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>开发学城04</title>
<style>
body {
margin: 0;
}
.container {
width: 1100px;
margin: 0 auto;
}
.menu {
height: 48px;
background-color: #499ef3;
line-height: 48px;
}
.menu a {
color: white;
text-decoration: none;
padding: 0 10px;
}
.course-list {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
}
.course-list .item {
width: 248px;
padding: 10px;
border: 1px solid #dddddd;
margin-right: 5px;
margin-top: 10px;
}
.course-list .item img {
width: 100%;
height: 120px;
}
</style>
<script src="vue.js"></script>
<script src="vue-router.js"></script>
</head>
<body>
<div id="app">
<div class="menu">
<div class="container">
<router-link to="/">开发学城</router-link>
<router-link to="/pins">沸点</router-link>
<router-link to="/home">首页</router-link>
<router-link to="/course">课程</router-link>
<router-link to="/news">咨询</router-link>
</div>
</div>
<div class="container">
<router-view></router-view>
</div>
</div>
<script>
const Home = {
data: function () {
return {
title: "欢迎使用开发学城"
}
},
template: `<h2>{{title}}</h2>`
}
const Course = {
data: function(){
return {
courseList: []
}
},
created: function(){
/* 组件创建完成之后自动触发【此时组件的对象已创建,但还未将页面相关的DOM创建并显示在页面上】
- 可以去操作组件对象,例如:this.courseList = [11,22,33]
- 不可以去操作DOM,例如:document.getElementById (未创建)
实际会用 axios({请求数据})
*/
this.courseList = [
{id: 111, name: "Golang从入门到精通", cover: "http://127.0.0.1:5500/src/img/go.png"},
{id: 222, name: "Python从入门到精通", cover: "http://127.0.0.1:5500/src/img/py.png"},
{id: 333, name: "DBA进阶之旅之MySQL", cover: "http://127.0.0.1:5500/src/img/mysql.png"},
{id: 444, name: "DBA进阶之旅之Redis", cover: "http://127.0.0.1:5500/src/img/redis.png"},
]
},
mounted: function () {
/* DOM对象已在页面上生成,此时就可以 */
},
template: `
<div class="course-list">
<div class="item" v-for="item in courseList">
<router-link :to="{name: 'Detail', params: {id: item.id}}">
<img :src="item.cover" alt="">
</router-link>
<a>{{item.name}}</a>
</div>
</div>
`
}
const Detail = {
// data: function(){
// return {
// title: "详细页面",
// paramDict: null,
// queryDict: null,
// }
// },
// created: function () {
// this.paramDict = this.$route.params;
// this.queryDict = this.$route.query;
// // 发送axios请求
// },
data: function(){
return {
title: "详细页面",
courseId: null,
hotCourseList: [
{id: 333, title: "DBA进阶之旅之MySQL"},
{id: 444, title: "DBA进阶之旅之Redis"},
],
}
},
created: function () {
this.courseId = this.$route.params.id;
// 此处可以根据课程ID,发送ajax请求获取课程详细信息
this.getCourseDetail();
},
watch: {
$route:function(to, from) {
this.courseId = to.params.id;
// this.getCourseDetail();
}
},
methods: {
getCourseDetail: function () {
// 根据this.courseId获取课程详细信息
}
},
template:`
<div>
<h2>课程详细页面</h2>
<div>当前课程ID为:{{courseId}}</div>
<h3>课程推荐</h3>
<ul>
<li v-for="item in hotCourseList">
<router-link :to="{name:'Detail', params:{id:item.id}}">{{item.title}}</router-link>
</li>
</ul>
</div>`
}
const News = {template:'<div><h2>咨询内容</h2></div>'}
const Pins = {
data: function(){
return {}
},
template: `
<div>
<h2>沸点专区</h2>
<router-link :to="{name:'Hot'}">热点</router-link>
<router-link :to="{name:'Following'}">关注</router-link>
<router-view></router-view>
</div>
`
}
const Hot = {template: `<div><h2>Hot页面</h2></div>`}
const Following = {template: `<div><h2>Following页面</h2></div>`}
const router = new VueRouter({
routes: [
{
path: "/",
component: Home,
},
{
path: "/pins",
component: Pins,
name: 'Pins',
children:[
{
// 当 /pins/hot 匹配成功,
// Hot组件 会被渲染在 Pins 的 <router-view> 中
path: 'hot',
component: Hot,
name:'Hot'
},
{
// 当 /pins/following 匹配成功,
// Following组件 会被渲染在 Pins 的 <router-view> 中
path: 'following',
component: Following,
name:'Following'
}
]
},
{
path: "/home",
component: Home,
},
{
path: "/course",
component: Course,
name: "Course",
},
{
path: "/detail/:id",
component: Detail,
name: "Detail",
},
{
path: "/news",
component: News,
}
]
})
var app = new Vue({
el: "#app",
data:{
},
router,
});
</script>
</body>
</html>
# 案例:后台分类菜单
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
body {
margin: 0;
}
.header {
height: 48px;
background-color: #499ef3;
line-height: 48px;
}
.header a {
color: white;
text-decoration: none;
padding: 0 10px;
}
.body .left-menu {
width: 180px;
border: 1px solid #dddddd;
border-bottom: 0;
position: absolute;
left: 1px;
top: 50px;
bottom: 0;
overflow: auto;
background-color: #f3f5f7;
}
.body .left-menu .head {
border-bottom: 1px solid #dddddd;
text-align: center;
font-size: 18px;
font-weight: bold;
padding: 15px;
}
.body .left-menu a {
display: block;
padding: 10px;
border-bottom: 1px solid #dddddd;
}
.body .right-body {
position: absolute;
left: 183px;
top: 50px;
right: 0;
bottom: 0;
overflow: auto;
padding: 10px;
}
</style>
<script src="vue.js"></script>
<script src="vue-router.js"></script>
<script src="axios.min.js"></script>
</head>
<body>
<div id="app">
<div class="header">
<router-link to="/">Logo</router-link>
<router-link to="/home">首页</router-link>
<router-link to="/task">任务宝</router-link>
<router-link to="/message">消息宝</router-link>
</div>
<div class="body">
<router-view></router-view>
</div>
</div>
<script>
const Home = {
data: function () {
return {
title: "欢迎使用xx系统"
}
},
template: `<h2>{{title}}</h2>`
};
const Task = {
data: function () {
return {}
},
template: `
<div>
<div class="left-menu">
<div class="head">任务宝</div>
<router-link :to="{name:'Fans'}">粉丝</router-link>
<router-link :to="{name:'Spread'}">推广码</router-link>
<router-link :to="{name:'Statistics'}">数据统计</router-link>
</div>
<div class="right-body">
<router-view></router-view>
</div>
</div>`
};
const Fans = {template: `<h3>粉丝页面</h3>`};
const Spread = {template: `<h3>推广码页面</h3>`};
const Statistics = {template: `<h3>数据统计页面</h3>`};
const Message = {
data: function () {
return {}
},
template: `
<div>
<div class="left-menu">
<div class="head">消息宝</div>
<router-link :to="{name:'Sop'}">SOP</router-link>
<router-link :to="{name:'Send'}">推送管理</router-link>
</div>
<div class="right-body">
<router-view></router-view>
</div>
</div>`
};
const Sop = {template: `<h3>SOP页面</h3>`};
const Send = {template: `<h3>推送管理页面</h3>`};
const router = new VueRouter({
routes: [
{path: '/', component: Home},
{path: '/home', component: Home},
{
path: '/task',
component: Task,
name: 'Task',
children: [
{
path: '',
// component: Fans,
// redirect:'/task/fans'
redirect: {name: 'Fans'}
},
{
path: 'fans',
component: Fans,
name: 'Fans'
},
{
path: 'spread',
component: Spread,
name: 'Spread'
},
{
path: 'statistics',
component: Statistics,
name: 'Statistics'
}
]
},
{
path: '/message',
component: Message,
name: 'Message',
children: [
{
path: 'sop',
component: Sop,
name: 'Sop'
},
{
path: 'send',
component: Send,
name: 'Send'
}
]
}
]
})
var app = new Vue({
el: '#app',
data: {},
methods: {},
router: router
})
</script>
</body>
</html>
# 4.7 编程式导航
除了使用 <router-link>
创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。
想要导航到不同的 URL,则使用 router.push
方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。
router.push
// 字符串 router.push('home') // 对象 router.push({ path: 'home' }) // 命名的路由 router.push({ name: 'user', params: { userId: '123' }}) // // 带查询参数,变成 /register?plan=private router.push({ path: 'register', query: { plan: 'private' }})
router.replace
// 字符串 router.replace('home') // 对象 router.replace({ path: 'home' }) // 命名的路由 router.replace({ name: 'user', params: { userId: '123' }}) // 带查询参数,变成 /register?plan=private router.replace({ path: 'register', query: { plan: 'private' }}) # 跟 router.push 很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。
router.go 这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步
// 在浏览器记录中前进一步,等同于 history.forward() router.go(1) // 后退一步记录,等同于 history.back() router.go(-1) // 前进 3 步记录 router.go(3) // 如果 history 记录不够用,那就默默地失败呗 router.go(-100) router.go(100)
# 案例:登录跳转(含顶部)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
body {
margin: 0;
}
.header {
height: 48px;
background-color: #499ef3;
line-height: 48px;
}
.header a {
color: white;
text-decoration: none;
padding: 0 10px;
}
</style>
<script src="vue.js"></script>
<script src="vue-router.js"></script>
</head>
<body>
<div id="app">
<div class="header">
<router-link to="/">Logo</router-link>
<router-link to="/home">首页</router-link>
<router-link to="/task">任务宝</router-link>
<router-link to="/message">消息宝</router-link>
<div style="float: right;">
<router-link to="/login">登录</router-link>
</div>
</div>
<div class="body">
<router-view></router-view>
</div>
</div>
<script>
const Home = {
data: function () {
return {
title: "欢迎使用xx系统"
}
},
template: `<h2>{{title}}</h2>`
};
const Task = {
data: function () {
return {}
},
template: `
<div>
<h2>任务宝页面</h2>
</div>`
};
const Message = {
data: function () {
return {}
},
template: `
<div>
<h2>消息宝页面</h2>
</div>`
};
const Login = {
data: function () {
return {
user: '',
pwd: ''
}
},
methods: {
doLogin: function () {
if (this.user.length > 0 && this.pwd.length > 0) {
this.$router.push({name: 'Task'});
// this.$router.replace({name: 'Task'});
}
}
},
template: `
<div style="width: 500px;margin: 100px auto">
<input type="text" placeholder="用户名" v-model="user"/>
<input type="text" placeholder="密码" v-model="pwd" />
<input type="button" value="提 交" @click="doLogin" />
</div>
`
};
const router = new VueRouter({
routes: [
{path: '/', component: Home},
{path: '/home', component: Home},
{path: '/login', component: Login, name: 'Login'},
{path: '/task', component: Task, name: 'Task'},
{path: '/message', component: Message, name: 'Message'}
]
})
var app = new Vue({
el: '#app',
data: {},
methods: {},
router: router
})
</script>
</body>
</html>
# 案例:登录跳转(不含顶部)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
body {
margin: 0;
}
.header {
height: 48px;
background-color: #499ef3;
line-height: 48px;
}
.header a {
color: white;
text-decoration: none;
padding: 0 10px;
}
.body .left-menu {
width: 180px;
border: 1px solid #dddddd;
border-bottom: 0;
position: absolute;
left: 1px;
top: 50px;
bottom: 0;
overflow: auto;
background-color: #f3f5f7;
}
.body .left-menu .head {
border-bottom: 1px solid #dddddd;
text-align: center;
font-size: 18px;
font-weight: bold;
padding: 15px;
}
.body .left-menu a {
display: block;
padding: 10px;
border-bottom: 1px solid #dddddd;
}
.body .right-body {
position: absolute;
left: 183px;
top: 50px;
right: 0;
bottom: 0;
overflow: auto;
padding: 10px;
}
</style>
<script src="vue.js"></script>
<script src="vue-router.js"></script>
</head>
<body>
<div id="app">
<router-view></router-view>
</div>
<script>
const Home = {
data: function () {
return {
title: "欢迎使用xx系统"
}
},
template: `
<div>
<div class="header">
<router-link to="/">Logo</router-link>
<router-link to="/home">首页</router-link>
<router-link :to="{name:'Task'}">任务宝</router-link>
<router-link :to="{name:'Message'}">消息宝</router-link>
<div style="float: right;">
<router-link to="/login">登录</router-link>
</div>
</div>
<div class="body">
<router-view></router-view>
</div>
</div>
`
};
const Index = {template: '<h3>这是个首页呀...</h3>'}
const Task = {
data: function () {
return {}
},
template: `
<div>
<div class="left-menu">
<div class="head">任务宝</div>
<router-link :to="{name:'Fans'}">粉丝</router-link>
<router-link :to="{name:'Spread'}">推广码</router-link>
<router-link :to="{name:'Statistics'}">数据统计</router-link>
</div>
<div class="right-body">
<router-view></router-view>
</div>
</div>`
};
const Fans = {template: `<h3>粉丝页面</h3>`};
const Spread = {template: `<h3>推广码页面</h3>`};
const Statistics = {template: `<h3>数据统计页面</h3>`};
const Message = {
data: function () {
return {}
},
template: `
<div>
<div class="left-menu">
<div class="head">消息宝</div>
<router-link :to="{name:'Sop'}">SOP</router-link>
<router-link :to="{name:'Send'}">推送管理</router-link>
</div>
<div class="right-body">
<router-view></router-view>
</div>
</div>`
};
const Sop = {template: `<h3>SOP页面</h3>`};
const Send = {template: `<h3>推送管理页面</h3>`};
const Login = {
data: function () {
return {
user: '',
pwd: ''
}
},
methods: {
doLogin: function () {
if (this.user.length > 0 && this.pwd.length > 0) {
this.$router.push({name: 'Index'});
// this.$router.replace({name: 'Task'});
}
}
},
template: `
<div style="width: 500px;margin: 100px auto">
<input type="text" placeholder="用户名" v-model="user"/>
<input type="text" placeholder="密码" v-model="pwd" />
<input type="button" value="提 交" @click="doLogin" />
</div>
`
};
const router = new VueRouter({
routes: [
{
path: '/',
// component: Home,
redirect: '/login'
},
{path: '/login', component: Login, name: 'Login'},
{
path: '/home',
component: Home,
children: [
{
path: '',
component: Index,
name: "Index"
},
{
path: 'task',
component: Task,
name: 'Task',
children: [
{
path: 'fans',
component: Fans,
name: 'Fans'
},
{
path: 'spread',
component: Spread,
name: 'Spread'
},
{
path: 'statistics',
component: Statistics,
name: 'Statistics'
}
]
},
{
path: 'message',
component: Message,
name: 'Message',
children: [
{
path: 'sop',
component: Sop,
name: 'Sop'
},
{
path: 'send',
component: Send,
name: 'Send'
}
]
}
],
},
]
})
var app = new Vue({
el: '#app',
data: {},
methods: {},
router: router
})
</script>
</body>
</html>
# 4.8 导航守卫
在基于vue-router实现访问跳转时,都会执行一个钩子。
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// to: Route: 即将要进入的目标 路由对象
// from: Route: 当前导航正要离开的路由
// next() 继续向后执行
// next(false) 中断导航,保持当前所在的页面。
// next('/') next({path:'/'}) next({name:'Login'}) 跳转到指定页面
})
注意:可以基于他实现未登录跳转登录页面。
# 案例:登录拦截(全局)
未登录时,访问后台管理页面,自动跳转到登录页面。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
body {
margin: 0;
}
.header {
height: 48px;
background-color: #499ef3;
line-height: 48px;
}
.header a {
color: white;
text-decoration: none;
padding: 0 10px;
}
.body .left-menu {
width: 180px;
border: 1px solid #dddddd;
border-bottom: 0;
position: absolute;
left: 1px;
top: 50px;
bottom: 0;
overflow: auto;
background-color: #f3f5f7;
}
.body .left-menu .head {
border-bottom: 1px solid #dddddd;
text-align: center;
font-size: 18px;
font-weight: bold;
padding: 15px;
}
.body .left-menu a {
display: block;
padding: 10px;
border-bottom: 1px solid #dddddd;
}
.body .right-body {
position: absolute;
left: 183px;
top: 50px;
right: 0;
bottom: 0;
overflow: auto;
padding: 10px;
}
</style>
<script src="vue.js"></script>
<script src="vue-router.js"></script>
</head>
<body>
<div id="app">
<router-view></router-view>
</div>
<script>
const Home = {
data: function () {
return {
title: "欢迎使用xx系统"
}
},
methods: {
doLogout: function () {
sessionStorage.clear();
this.$router.push({name: "Login"});
}
},
template: `
<div>
<div class="header">
<router-link to="/">Logo</router-link>
<router-link to="/home">首页</router-link>
<router-link :to="{name:'Task'}">任务宝</router-link>
<router-link :to="{name:'Message'}">消息宝</router-link>
<div style="float: right;">
<a @click="doLogout">注销</a>
</div>
</div>
<div class="body">
<router-view></router-view>
</div>
</div>
`
};
const Index = {template: '<h3>这是个首页呀...</h3>'}
const Task = {
data: function () {
return {}
},
template: `
<div>
<div class="left-menu">
<div class="head">任务宝</div>
<router-link :to="{name:'Fans'}">粉丝</router-link>
<router-link :to="{name:'Spread'}">推广码</router-link>
<router-link :to="{name:'Statistics'}">数据统计</router-link>
</div>
<div class="right-body">
<router-view></router-view>
</div>
</div>`
};
const Fans = {template: `<h3>粉丝页面</h3>`};
const Spread = {template: `<h3>推广码页面</h3>`};
const Statistics = {template: `<h3>数据统计页面</h3>`};
const Message = {
data: function () {
return {}
},
template: `
<div>
<div class="left-menu">
<div class="head">消息宝</div>
<router-link :to="{name:'Sop'}">SOP</router-link>
<router-link :to="{name:'Send'}">推送管理</router-link>
</div>
<div class="right-body">
<router-view></router-view>
</div>
</div>`
};
const Sop = {template: `<h3>SOP页面</h3>`};
const Send = {template: `<h3>推送管理页面</h3>`};
const Login = {
data: function () {
return {
user: '',
pwd: ''
}
},
methods: {
doLogin: function () {
if (this.user.length > 0 && this.pwd.length > 0) {
sessionStorage.setItem("isLogin", true);
this.$router.push({name: 'Index'});
}
}
},
template: `
<div style="width: 500px;margin: 100px auto">
<input type="text" placeholder="用户名" v-model="user"/>
<input type="text" placeholder="密码" v-model="pwd" />
<input type="button" value="提 交" @click="doLogin" />
</div>
`
};
const router = new VueRouter({
routes: [
{
path: '/',
component: Home,
redirect: '/home'
},
{
path: '/home',
component: Home,
name: "Home",
children: [
{
path: '',
component: Index,
name: "Index"
},
{
path: 'task',
component: Task,
name: 'Task',
children: [
{
path: 'fans',
component: Fans,
name: 'Fans'
},
{
path: 'spread',
component: Spread,
name: 'Spread'
},
{
path: 'statistics',
component: Statistics,
name: 'Statistics'
}
]
},
{
path: 'message',
component: Message,
name: 'Message',
children: [
{
path: 'sop',
component: Sop,
name: 'Sop'
},
{
path: 'send',
component: Send,
name: 'Send'
}
]
}
],
},
{path: '/login', component: Login, name: 'Login'},
]
})
router.beforeEach((to, from, next) => {
// 如果已登录,则可以继续访问目标地址
if (sessionStorage.getItem('isLogin')) {
next();
return;
}
// 未登录,访问登录页面
if (to.name === "Login") {
next();
return;
}
// 未登录,跳转登录页面
// next(false); 保持当前所在页面,不跳转
next({name: 'Login'});
})
var app = new Vue({
el: '#app',
data: {},
methods: {},
router: router
})
</script>
</body>
</html>
# 案例:登录拦截(路由)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
body {
margin: 0;
}
.header {
height: 48px;
background-color: #499ef3;
line-height: 48px;
}
.header a {
color: white;
text-decoration: none;
padding: 0 10px;
}
.body .left-menu {
width: 180px;
border: 1px solid #dddddd;
border-bottom: 0;
position: absolute;
left: 1px;
top: 50px;
bottom: 0;
overflow: auto;
background-color: #f3f5f7;
}
.body .left-menu .head {
border-bottom: 1px solid #dddddd;
text-align: center;
font-size: 18px;
font-weight: bold;
padding: 15px;
}
.body .left-menu a {
display: block;
padding: 10px;
border-bottom: 1px solid #dddddd;
}
.body .right-body {
position: absolute;
left: 183px;
top: 50px;
right: 0;
bottom: 0;
overflow: auto;
padding: 10px;
}
</style>
<script src="vue.js"></script>
<script src="vue-router.js"></script>
</head>
<body>
<div id="app">
<router-view></router-view>
</div>
<script>
const Home = {
data: function () {
return {
title: "欢迎使用xx系统"
}
},
methods: {
doLogout: function () {
sessionStorage.clear();
this.$router.push({name: "Login"});
}
},
template: `
<div>
<div class="header">
<router-link to="/">Logo</router-link>
<router-link to="/home">首页</router-link>
<router-link :to="{name:'Task'}">任务宝</router-link>
<router-link :to="{name:'Message'}">消息宝</router-link>
<div style="float: right;">
<a @click="doLogout">注销</a>
</div>
</div>
<div class="body">
<router-view></router-view>
</div>
</div>
`
};
const Index = {template: '<h3>这是个首页呀...</h3>'}
const Task = {
data: function () {
return {}
},
template: `
<div>
<div class="left-menu">
<div class="head">任务宝</div>
<router-link :to="{name:'Fans'}">粉丝</router-link>
<router-link :to="{name:'Spread'}">推广码</router-link>
<router-link :to="{name:'Statistics'}">数据统计</router-link>
</div>
<div class="right-body">
<router-view></router-view>
</div>
</div>`
};
const Fans = {template: `<h3>粉丝页面</h3>`};
const Spread = {template: `<h3>推广码页面</h3>`};
const Statistics = {template: `<h3>数据统计页面</h3>`};
const Message = {
data: function () {
return {}
},
template: `
<div>
<div class="left-menu">
<div class="head">消息宝</div>
<router-link :to="{name:'Sop'}">SOP</router-link>
<router-link :to="{name:'Send'}">推送管理</router-link>
</div>
<div class="right-body">
<router-view></router-view>
</div>
</div>`
};
const Sop = {template: `<h3>SOP页面</h3>`};
const Send = {template: `<h3>推送管理页面</h3>`};
const Login = {
data: function () {
return {
user: '',
pwd: ''
}
},
methods: {
doLogin: function () {
if (this.user.length > 0 && this.pwd.length > 0) {
sessionStorage.setItem("isLogin", true);
this.$router.push({name: 'Index'});
}
}
},
template: `
<div style="width: 500px;margin: 100px auto">
<input type="text" placeholder="用户名" v-model="user"/>
<input type="text" placeholder="密码" v-model="pwd" />
<input type="button" value="提 交" @click="doLogin" />
</div>
`
};
const router = new VueRouter({
routes: [
{
path: '/',
component: Home,
redirect: '/home'
},
{
path: '/home',
component: Home,
name: "Home",
children: [
{
path: '',
component: Index,
name: "Index"
},
{
path: 'task',
component: Task,
name: 'Task',
children: [
{
path: 'fans',
component: Fans,
name: 'Fans'
},
{
path: 'spread',
component: Spread,
name: 'Spread'
},
{
path: 'statistics',
component: Statistics,
name: 'Statistics'
}
]
},
{
path: 'message',
component: Message,
name: 'Message',
children: [
{
path: 'sop',
component: Sop,
name: 'Sop'
},
{
path: 'send',
component: Send,
name: 'Send'
}
]
}
],
beforeEnter: (to, from, next) => {
if (sessionStorage.getItem('isLogin')) {
next();
return;
}
// 未登录,跳转登录页面
// next(false); 保持当前所在页面,不跳转
next({name: 'Login'});
}
},
{path: '/login', component: Login, name: 'Login'},
]
})
var app = new Vue({
el: '#app',
data: {},
methods: {},
router: router
})
</script>
</body>
</html>
# 补充:前端存储
- cookie 有时效,可自动携带到发送请求中
- localStorage 可一直存在 指导手动清空
setItem (key, value) getItem (key) removeItem (key) clear () key (index)
- sessionStorage 页面关闭或浏览器关闭 清空