Laravel5与前端(三)导航和状态管理

侧边导航

我们现在知道,单页应用中的每一页对应到一个vue组件,那么总会有一些公共组件,例如导航,那这些公共组件在单页中应该怎么实现呢。我们先直接把导航的代码写出来试试。

版本1:先实现导航功能

html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="app">
<h1>导航</h1>
<ul class="menu">
<li>
<router-link to="/user">用户管理</router-link>
</li>
<li>
<router-link to="/course">课程管理</router-link>
</li>
</ul>
<div>
<h2>首页</h2>
<router-view></router-view>
</div>
</div>

app.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Vue from 'vue'
import VueRouter from 'vue-router'

const UserPage = () => import('./modules/User')
const CoursePage = () => import('./modules/Course')

Vue.use(VueRouter)

const routes = [
{ path: '/user', component: UserPage },
{ path: '/course', component: CoursePage }
]

const router = new VueRouter({
base: '/',
routes // (缩写)相当于 routes: routes
})


const app = new Vue({
el: '#app',
router
});

此时已经可以通过导航来切换不同的页面了。

我们可以发现这里少了两个东西:导航菜单没有点亮、导航标题没有根据路由变化来进行切换。

版本2:点亮菜单项

为了方便,我们把导航单独做成组件放在 components/particals/Sidebar.vue 文件中,把路由做成模块放在 router 目录下,把 导航标题 做成组件放在 components/particals/Header.vue,完成后结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
.
├── app.js
├── bootstrap.js
├── components
│   ├── ExampleComponent.vue
│   └── particals
│   ├── NavHeader.vue
│   └── Sidebar.vue
├── modules
│   ├── Course.vue
│   └── User.vue
└── router
└── index.js

此时app.js内容为:

1
2
3
4
5
6
7
8
9
10
11
12
require('./bootstrap');

import Vue from 'vue'
import router from './router'

Vue.component('sidebar', () => import('./components/particals/Sidebar'))
Vue.component('nav-header', () => import('./components/particals/NavHeader'))

const app = new Vue({
el: '#app',
router
});

我们观察生成的html页面,发现生成的路由链接中,会自动的挂上一些标记:

这其实是vue-router的一个功能:https://router.vuejs.org/zh-cn/api/options.html#linkactiveclass

所以,我们只需在 app.scss 文件中添加如下代码即可:

1
2
3
4
5
6
7
.menu a.router-link-active, .menu a.router-link-exact-active {
color: #0a6cd6;
}

.menu a{
color: #000;
}

版本3:切换标题

我们先来看看 NavHeader.vue 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<div>
<h2 v-text="title"></h2>
</div>
</template>

<script>
export default {
data() {
return {
title: '首页'
}
}
}
</script>

可以看到,组件内有一个 title 变量,我们在切换路由时,如果可以改变这个变量值就好了,Prop可以做到这一点:https://cn.vuejs.org/v2/guide/components.html#Prop

我们需要把 NavHeader.vue script标签内代码改成:

1
2
3
export default {
props: ['title']
}

spa.blade.php 前端模板文件中,传入 title 值:

1
<nav-header title="首页2"></nav-header>

现在这个值只是一个字面量,我们把它改成动态值。先修改 app.js,在里面加上navTitle 变量:

1
2
3
4
5
6
7
8
9
const app = new Vue({
el: '#app',
data() {
return {
navTitle: '首页'
}
},
router
});

修改模板文件,将 title 绑定为 navTitle 变量:

1
<nav-header :title="navTitle"></nav-header>

好了,在路由切换时怎么变更标题呢?在router/index.js 中加入以下代码:

1
2
3
4
5
6
7
8
router.beforeEach((to, from, next) => {
if (to.fullPath == '/user') {
this.a.app.navTitle = '用户管理'
} else if (to.fullPath == '/course') {
this.a.app.navTitle = '课程管理'
}
next()
})

beforeEach 函数会在路由切换时触发

vuex

在公共组件过多时,公共组件在各个状态之间时会变得比较混乱,而vuex就是为解决这个问题而诞生的,我们现在使用vuex来实现 导航标题 的切换。

为了方便起见,我们将导航作为配置项,放在 config 目录下,并通过 name 属性来引用页面:

config/menu.js

1
2
3
4
5
6
7
8
9
export default [
{
text: '用户管理',
uri: { name: 'user'}
}, {
text: '课程管理',
uri: { name: 'course'}
}
]

同时修改 Sidebar.vue 内容为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<ul class="menu">
<li v-for="item in menus">
<router-link :to="item.uri" v-text="item.text"></router-link>
</li>
</ul>
</template>

<script>
import menu from "../../config/menu";

export default {
data() {
return {
menus: menu
}
}
}
</script>

同时在定义路由时,为他们添加导航标题(meta.title):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const routes = [
{
path: '/user',
name: 'user',
meta: { title: '导航标题:用户管理' },
component: () => import('../modules/User')
},
{
path: '/course',
name: 'course',
meta: { title: '导航标题:课程管理' },
component: () => import('../modules/Course')
}
]

安装

1
npm install vuex

使用

我们在建立 vuex/store.js 文件,用来保存一些公共的状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
state: {
navTitle: '首页'
},
mutations: {
changeNavTitle(state, title) {
state.navTitle = title
}
}
})

export default store

上述代码定义了一个状态 navTitle 表示导航标题,并提供了一个函数 changeNavTitle 来修改这个状态。

app.js 中,将期引入到Vue中去:

1
2
3
4
5
6
7
8
...
import store from './vuex/store'
...
const app = new Vue({
...
store
...
});

当然也需要修改 NavHeader.vue 让他把状态 navTitle显示出来:

(记得把前端模板文件 spa.blade.php 里面的 :title="navTitle" 去掉)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<div>
<h2 v-text="title"></h2>
</div>
</template>

<script>
export default {
computed: {
title() {
return this.$store.state.navTitle
}
}
}
</script>

随路由切换

我们修改 beforeEach 函数:

1
2
3
4
5
6
7
8
...
import store from '../vuex/store'
...
router.beforeEach((to, from, next) => {
store.commit('changeNavTitle', to.meta.title)
next()
})
...

store.commit 函数会调用在 store.jsmutation 定义的方法,这也是官方推荐的修改状态的方式。

意思是,你也可以使用 store.state.navTitle = to.meta.title 来直接修改状态,但官方不推荐这么做。

最后附上项目文件结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.
├── app.js
├── bootstrap.js
├── components
│   ├── ExampleComponent.vue
│   └── particals
│   ├── NavHeader.vue
│   └── Sidebar.vue
├── config
│   └── menu.js
├── modules
│   ├── Course.vue
│   └── User.vue
├── router
│   └── index.js
└── vuex
└── store.js
坚持原创文章分享,您的支持将鼓励我继续创作!