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


侧边导航

我们现在知道,单页应用中的每一页对应到一个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.jsmutation 定义的方法,这也是官方推荐的修改状态的方式。

意思是,你也可以使用 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

文章作者: jerrycheese
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 jerrycheese !
  目录