Vue-router

第一步、安装vue-router

默认下载的是4.0,但是我不知道怎么用,所以降级使用

使用版本:vue cli 3.0+,vue 2.0+

目录地址:src/router/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import About from "@/views/About";

Vue.use(VueRouter)

const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',

component: About
}
]

const router = new VueRouter({
routes
})

export default router

main.js

1
2
3
4
5
6
7
8
9
10
11
import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

new Vue({
router,
render: h => h(App)
}).$mount('#app')

app.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
<router-view/>
</div>
</template>

<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}

#nav {
padding: 30px;
}

#nav a {
font-weight: bold;
color: #2c3e50;
}

#nav a.router-link-exact-active {
color: #42b983;
}
</style>

其中router-link是路由,默认会被渲染成a标签,router-view是渲染的位置

修改默认属性:

history和hash

1
2
3
4
const router = new VueRouter({
routes,
mode: "history"
})

vue-link其他属性

tag:想要把这个标签渲染成啥标签

1
<router-link to="/home" tag="button">home</router-link>

replace:替换默认的hash,history的put方法,不会有后退一步的操作

测试如果显示不出来,就重启项目

1
<router-link to="/home" tag="button" replace>home</router-link>

linkactiveclass:修改点击激活的class

1
2
3
4
5
const router = new VueRouter({
routes,
mode: "history",
linkActiveClass:"active"
})

使用代码修改路由

this.$router.push(“path”)或者this.$router.replace(“path”)

1
<button @click="go">代码跳转</button>
1
2
3
go() {
this.$router.push("/home")
}

动态路由

就是类似于restful风格的东西,我们的信息,以前的java是**/get/{params}**,就是我们的动态路由

index.js

1
2
3
4
{
path: "/user/:userId",
component: User
}

user.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div>
<div>欢迎你!{{ Info }}</div>
</div>
</template>

<script>
export default {
name: "User",
computed: {
Info() {
return this.$route.params.userId;
}
}
}
</script>

<style scoped>

</style>

跳转控制页面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<template>
<div>
<div>
<router-link to="/home" tag="button" replace>
home
</router-link>

<router-link to="/about" tag="button" replace>
about
</router-link>
<button @click="go">代码跳转</button>
<router-link :to="'/user/'+userId">用户</router-link>
<input type="text" v-model="userId">
<router-view/>
</div>
</div>
</template>

<script>
export default {
name: "cpn",
data() {
return {
userId: "saxon"
}
},
methods: {
go() {
this.$router.push("/home")
}
},
};
</script>

<style scoped>
</style>

关键代码:

1
<router-link :to="'/user/'+userId">用户</router-link>

使用v-bind绑定组件数据

路由懒加载

懒加载的目的就是用到在加载,我们测试的方法就是,先再主页,看看请求的资源是啥,然后我们运用懒加载以后再看一遍有没有添加的资源,就明白懒加载了

router:index.js

1
2
3
4
5
const User=()=>import("./../components/User")
{
path: "/user/:userId",
component: User
}

这样做以后,它会在打包的时候把我们的组件进行分组,然后用到组件在加载

嵌套路由

router:index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
{
path: "/user/:userId",
component: User,
children: [
{
path: "info",
component: () => import("./../components/UserInfo")
}, {
path: "school",
component: () => import("./../components/UserSchool")
}
]
}

user.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<div>
<div>欢迎你!{{ Info }}</div>
<router-link :to="'/user/'+Info+'/info'">个人信息</router-link>
<router-link :to="'/user/'+Info+'/school'">学校信息</router-link>
<router-view/>
</div>
</template>

<script>
export default {
name: "User",
computed: {
Info() {
return this.$route.params.userId;
}
}
}
</script>

<style scoped>

</style>

全局路由导航守卫

需求:再我点击那个组件的时候,就再title哪里显示我当前页面的标题;

第一步、配置元数据meta;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
{
path: "/",
redirect: "/home",
meta: {
title: "首页"
}
},
{
path: '/home',
name: 'Home',
component: Home,
meta: {
title: "首页"
}
},
{
path: '/about',
name: 'About',
component: About,
meta: {
title: "关于"
}
},
{
path: "/user/:userId",
component: User,
meta: {
title: "用户"
},
children: [
{
path: "info",
component: () => import("./../components/UserInfo")
}, {
path: "school",
component: () => import("./../components/UserSchool")
}
]
}

接下来,我们使用route.beforeEach()函数实现我们的功能需求;

1
2
3
4
router.beforeEach((to, from, next) => {
document.title = to.matched[0].meta.title;
next();
})

我们可以把我们要用到的对象先打印一下,看一下他们分别是什么东西;

image-20210204143853276

从这里我们可以看到to是一个对象就是一个一个的route,我们获得的matched就是我们再index里面配置的属性,那么我们原本的meta里面也有title,为什么不直接使用我们的to.meta.title呢,那是由于如果我们是嵌套路由的话,他获得的就会不准确,为了每一次都可以准确的获得title,我们就使用前面的方法来获得属性

keep-alive属性

需求:我点击一个组件有嵌套路由,我下次再访问我也想一访问就可以到上次访问的页面,不用再点击一次子菜单;

TIp:再你离开这个组件的时候保留当前的路由信息,下次在访问的时候就可以使用这个路由跳转;

当我们离开组件的时候,默认是把组件销毁了的,如果我们想要避免他销毁组件,提高我们的效率,我们就是要使用keep-alive包裹router-view,这样下次访问的时候就不会再次创建组件;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//创建组件时调用回调函数
created() {
console.log('create');
},
//销毁组件时调用函数
destroyed() {
console.log('destroyed');
},
//有keep alive包裹的时候,激活回调
activated() {
console.log('activated');
},
//有keep alive包裹的时候,不激活回调
deactivated() {
console.log('deactivated');
}

输出显示:

再组件创建以后,来回点击用户,发现它就不再创建组件 如果没有keepalive的话,activated和deactivated就不会生效

image-20210204154416854

,所以为了实现我们刚才的内容,我们可以利用这几个函数老实现对router的保存

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
export default {
name: "User",
data() {
return {
path: "/user/saxon/info"
}
},
computed: {
Info() {
return this.$route.params.userId;
}
},
//有keep alive包裹的时候,激活回调
activated() {
console.log("active")
//在激活的时候,跳到上一次访问的地方
this.$router.push(this.path)
},
beforeRouteLeave(to, from, next) {
next()
this.path = from.path;
},
}

如果我们使用的是,destroy的话,拿到的路由信息太晚,于是可以使用beforeRouteLeave来实现这一个方法

排除某些组件,可以再keep alive加上exclude,

1
2
3
<keep-alive exclude="User">
<router-view/>
</keep-alive>

简单项目实战:

创建一个tabbar组件:

image-20210205212053937

没激活的灰色激活的变色;

main.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import Vue from 'vue'
import App from "@/App";
import router from './router'


Vue.config.productionTip = false


new Vue({
router,
render: h => h(App)
}).$mount('#app')


app.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<template>
<div id="app">
<keep-alive>
<router-view/>
</keep-alive>
<tabbar>
<div v-for="item in items">
<tabbat-item :item="item.name" :path="item.path"></tabbat-item>
</div>
</tabbar>
</div>
</template>
<script>
import tabbar from "@/components/tabbar/tabbar";
import tabbatItem from "@/components/tabbar/tabbatItem";


export default {
name: "App",
data() {
return {
items: [
{path: "home", name: "首页"},
{path: "shoppingCar", name: "购物车"},
{path: "type", name: "种类"},
{path: "about", name: "关于"}]
}
},
components: {
tabbar,
tabbatItem
}
}
</script>

<style>
a {
text-decoration: none;

}

.active {
color: red;
}
</style>

tarbar.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<div class="tabbar">
<slot></slot>
</div>
</template>

<script>
export default {
name: "tabbar",
}
</script>

<style scoped>

.tabbar{
position: fixed;
display: flex;
left: 0;
right: 0;
bottom: 0;
}
</style>

tabbarItem.vue:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<template>
<div>
<div class="tabbarItem" @click="itemClick">
<img v-if="isActive" :src=imgPath>
<img v-else :src=imgPath2>
<div :class="{active:isActive}">{{ item }}</div>
</div>
</div>
</template>

<script>
export default {
name: "tabbatItem",
data() {
return {}
},
methods: {
itemClick() {
this.$router.replace(this.path)
}
},
computed: {
isActive() {
return this.$route.path.indexOf(this.path) !== -1
},
imgPath() {
return require("../../assets/img/" + this.path + ".svg")
},
imgPath2() {
return require("../../assets/img/" + this.path + "-1.svg")
},

},
props: {
item: {
type: String
},
path: {
type: String
}
}
}
</script>

<style scoped>
.tabbarItem {
display: flex;
text-align: center;
margin-left: 15px;
}

img {
position: relative;
width: 24px;
height: 24px;
top: -24px;
left: 30px;
}
</style>

菜单项

特别需要注意的是,如果使用图片的src绑定的话,需要使用require();

显示页:

home.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div>
<div>this is home page</div>

</div>
</template>

<script>
export default {
name: "home",
}
</script>

<style scoped>

</style>

路由:

index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import Vue from 'vue'
import VueRouter from 'vue-router'

import home from "@/views/home";
import about from "@/views/about";
import shoppingCar from "@/views/shoppingCar";
import type from "@/views/type";

Vue.use(VueRouter)

const routes = [
{
path: "",
redirect: "/home"
},
{
path: "/home",
component: home
},
{
path: "/type",
component: type
}, {
path: "/about",
component: about
}, {
path: "/shoppingCar",
component:shoppingCar
}

]

const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes,
linkActiveClass: "active"
})

export default router

这个写的还不够完善,我们必须明确一个封装的思想就是不要使用者来修改你的代码就可以实现功能,我写的这个虽然页面效果实现了,但是模块间太过于冗余和复杂度,不是一个好的组件,如果以后自己写组件的时候,我们就要把一切封装,把所有的需要的配置全部透明,以一个属性的样式让用户使用,这样才算一个合格的组件;

给路径取别名

vue.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const path = require('path');//引入path模块
function resolve(dir) {
return path.join(__dirname, dir)//path.join(__dirname)设置绝对路径
}

module.exports = {
chainWebpack: (config) => {
config.resolve.alias
.set('@', resolve('./src'))
.set('components', resolve('./src/components'))
.set('views', resolve('src/views'))
.set('assets', resolve('src/assets'))
}
}

使用:

1
2
import tabbar from "components/tabbar/tabbar";
import tabbatItem from "components/tabbar/tabbatItem";