侧边导航
我们现在知道,单页应用中的每一页对应到一个vue组件,那么总会有一些公共组件,例如导航,那这些公共组件在单页中应该怎么实现呢。我们先直接把导航的代码写出来试试。
版本1:先实现导航功能
html:
<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:
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
,完成后结构如下:
.
├── app.js
├── bootstrap.js
├── components
│ ├── ExampleComponent.vue
│ └── particals
│ ├── NavHeader.vue
│ └── Sidebar.vue
├── modules
│ ├── Course.vue
│ └── User.vue
└── router
└── index.js
此时app.js内容为:
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
文件中添加如下代码即可:
.menu a.router-link-active, .menu a.router-link-exact-active {
color: #0a6cd6;
}
.menu a{
color: #000;
}
版本3:切换标题
我们先来看看 NavHeader.vue
文件:
<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标签内代码改成:
export default {
props: ['title']
}
在 spa.blade.php
前端模板文件中,传入 title
值:
<nav-header title="首页2"></nav-header>
现在这个值只是一个字面量,我们把它改成动态值。先修改 app.js
,在里面加上navTitle
变量:
const app = new Vue({
el: '#app',
data() {
return {
navTitle: '首页'
}
},
router
});
修改模板文件,将 title
绑定为 navTitle
变量:
<nav-header :title="navTitle"></nav-header>
好了,在路由切换时怎么变更标题呢?在router/index.js
中加入以下代码:
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
:
export default [
{
text: '用户管理',
uri: { name: 'user'}
}, {
text: '课程管理',
uri: { name: 'course'}
}
]
同时修改 Sidebar.vue
内容为:
<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
):
const routes = [
{
path: '/user',
name: 'user',
meta: { title: '导航标题:用户管理' },
component: () => import('../modules/User')
},
{
path: '/course',
name: 'course',
meta: { title: '导航标题:课程管理' },
component: () => import('../modules/Course')
}
]
安装
npm install vuex
使用
我们在建立 vuex/store.js
文件,用来保存一些公共的状态:
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中去:
...
import store from './vuex/store'
...
const app = new Vue({
...
store
...
});
当然也需要修改 NavHeader.vue
让他把状态 navTitle
显示出来:
(记得把前端模板文件 spa.blade.php
里面的 :title="navTitle"
去掉)
<template>
<div>
<h2 v-text="title"></h2>
</div>
</template>
<script>
export default {
computed: {
title() {
return this.$store.state.navTitle
}
}
}
</script>
随路由切换
我们修改 beforeEach
函数:
...
import store from '../vuex/store'
...
router.beforeEach((to, from, next) => {
store.commit('changeNavTitle', to.meta.title)
next()
})
...
store.commit
函数会调用在 store.js
中 mutation
定义的方法,这也是官方推荐的修改状态的方式。
意思是,你也可以使用 store.state.navTitle = to.meta.title
来直接修改状态,但官方不推荐这么做。
最后附上项目文件结构:
.
├── 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