SpringBoot + Vue 微人事项目(第五天)
Vuex 介绍 与安装
菜单数据不仅仅是要在Home.vue,Login.vue里面使用,还需要在很多很多vue页面里面使用,所以要放在一个所有vue文件都可以访问的地方。Home.vue和Login.vue里面的data只能在各自的页面使用 ,这些data都是局部变量,我要做的事是把加载后的数据放到一个公共的地方,不管是Home.vue还是其他组件都能访问到的地方,放到sessionStorang和localStorage里面是可以访问到的,但是还可以放到vuex
什么时候需要状态管理?
当我们的项目很大的时候,这个vuex肯定是不可避免需要的,因为我的这些.vue组件它不可能是完全独立的,互相之间肯定有互相调用的。
如下面的V部落图片里面的"已发表",“草稿箱”,"回收站"这三个里面的数据其实在数据库里面加载的是同一张表,只不过表里面有一个记录状态码不一样
什么是Vuex?
Vuex 是一个专为 Vue.js
应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到
Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel
调试、状态快照导入导出等高级调试功能。
Vuex如何安装?
使用npm install vuex命令进行下载安装
Vuex的配置
①.首先在src目录里面创建一个store目录,然后在store目录里面创建index.js
②.首先要导入vue对象和vuex对象,然后用vue对象.use使用vuex
//导入vue对象
import Vue from 'vue'
//导入vuex
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
//定义变量的
state:{
//这是我的路由数组,一会从服务器上加载下来的菜单项都放在这个数组里面去
routes:[]
},
//修改变量的方法 想当于是java中的setter方法
mutations:{
//通过方法放 state:要通过state才可以访问到上面的state变量里面的routes数组 data:传进来的参数 等下去调用只需要传data参数即可
initRoutes(state,data){
state.routes=data;
}
},
actions:{
}
})
定义好之后还不能直接使用,还需要在mian.js里面使用导入语句导入进来
菜单请求工具类封装
菜单的请求方法包括前端数据的格式化
原因:服务端的菜单数据已经写好了,但是不能直接给前端用,因为服务端数据中的component是一个字符串,而我们在router里面定义的component都是导入的对象。所以需要写一个菜单请求工具类
component是一个字符串
router里面定义的component都是导入的对象
在utils文件夹里面新建一个menu.js文件
import {getRequest} from "./api";
//工具类方法
//定义一个菜单初始化的方法 参数router:因为菜单格式化好后要加到router里面来,所以要把router对象给我 store:这个数据我还要往store保存一份
export const initMenu = (router, store) => {
if (store.state.routes.length > 0) {//判断Vuex里数组是否有数据,如果有就直接return结束,否则就执行后面的代码
return;
}
//url:请求接口 data:请求回来的数据
getRequest("/system/config/menu").then(data => {
if (data) {
//定义一个fmtRoutes 这是格式化之后的,调用自己定义的formatRoutes方法把服务端返回的数据进行格式化数据
let fmtRoutes = formatRoutes(data.object);
//把router add 到fmtRoutes里面来
router.addRoutes(fmtRoutes);
//通过store.commit 第一个参数是方法的名字 第二个是格式化之后的fmtRoutes
store.commit('initRoutes', fmtRoutes)
}
})
}
//格式化
export const formatRoutes = (routes) => {
//这是我一会要返回去的数据
let fmRoutes = [];
routes.forEach(router => { //routers进行遍历 router就是这个数组的每一项
//这是一个批量的变量定义相当于等于就是let path = router.path
let {
path,
component,
name,
meta,
iconCls,
children
} = router;
//判断是不是一个的children
if (children && children instanceof Array) {
//递归调用给它格式化一下
children = formatRoutes(children);
}
let fmRouter = {
path: path,//上面path的值
name: name,
iconCls:iconCls,
meta: meta,
children: children,
//要动态的去加载component 当成一个方法来处理,里面有一个参数叫resolve
component(resolve) {
if (component.startWith("Emp")) {//判断是否以Emp开始的
require(['../views/emp/' + component + '.vue'], resolve); //运行时调用,相当于导入组件
} else if (component.startWith("Per")) {
require(['../views/per/' + component + '.vue'], resolve);
} else if (component.startWith("Sal")) {
require(['../views/sal/' + component + '.vue'], resolve);
} else if (component.startWith("Sta")) {
require(['../views/sta/' + component + '.vue'], resolve);
} else if (component.startWith("Sys")) {
require(['../views/sys/' + component + '.vue'], resolve);
}
//这是ES5里面的写法 []:请求组件的地址
}
}
fmRoutes.push(fmRouter);
})
return fmRoutes;
}
根据数据库的component把其他的vue页面也创建出来
左边导航菜单加载
工具类的方法已经写好了,那么什么时候去调用它?有人说登录之后调用 ,这个是没问题的,但是用户按了F5或者是浏览器上的刷新按钮,所有的菜单都没了,这个是不行的。存储在vuex的数据只要浏览器刷新就会没有了
解决方法1:可以使用vue的路由导航守卫
解决办法2:也可以把数据存储到localStorage里
全局前置守卫
你可以使用router.beforeEach注册一个全局前置守卫:
const router = new VueRouter({...})
router.beforeEach((to,from,next) = > ){
//...
}
全局前置守卫是 Vue Router 中最常用的路由守卫之一。它会在路由切换之前进行拦截和控制,以满足不同的业务需求
每个守卫方法接收三个参数:
- to: Route:即将要进入的目标 路由对象
- from:Route:当前导航正要离开的路由
- next:Function:一定要调用该方法resolve这个钩子,执行效果依赖next方法的调用参数
因为这个是全局前置守卫,所以要写在main.js文件里面,写在Vue.use(ElementUI)后面、
router.beforeEach((to,from,next)=>{ //全局前置守卫路由切换之前进行拦截和控制
//如果你要去的页面是login页,直接next
if (to.path=='/'){
next();
}else {
//如果你要去的是其他页面呢,就是用initMenu初始化菜单的方法
initMenu(router,store);
next();
}
})
如果要去的是login页面,直接next,如果要去的是其他页面,就要使用inintMenu方法进行初始化,我们之前就在menus.js里面进行判断了,数据判断的之后,我都会把数据保存在store里面,所以一上来我就先判断store里面有没有数据,如果store里面有数据的话,那就说明这是一次正常的跳转,不是用户按了F5的刷新,正常的跳转后面的getRequest请求就不用执行了
个initMenu方法执行了之后,这个数据现在就保存在store的routes里面去了,现在就可以在Home.vue里面使用了
<el-submenu :index="index+''" v-for="(item,index) in this.$router.options.routes" v-if="!item.hidden" :key="index"> <!--这个遍历拿到的是index.js里面的routers地址数组 -->
<template slot="title">
<i style="color: #409eff;margin-right: 10px" :class="item.iconCls"></i>
<span>{{item.name}}</span>
</template>
<el-menu-item-group>
<el-menu-item :index="child.path" v-for="(child,index) in item.children" :key="index">{{child.name}}</el-menu-item>
</el-menu-item-group>
</el-submenu>
我们现在用的是this.$router.options.routes,要换成我们在store里面存的数据
在store里面,我们习惯使用computed计算属性来列出store里面的数据
computed:{
routes(){
return this.$store.state.routes;
}
然后在上面渲染的时候就可以直接使用v-for=“(index,item) in routes”
如果报这个错
就打印看看这个里面传的数据有没有问题
动态渲染了菜单导航栏之后,点击子菜单进不去里面的页面,这是因为没有在menus.js里面进行判断,判断一下就可以点进其他的页面了
component(resolve) {
if (component.startsWith("Home")){
require(['../views/'+component+'.vue'],resolve);
} else if (component.startsWith("Emp")) {
require(['../views/emp/' + component + '.vue'], resolve);
} else if (component.startsWith("Per")) {
require(['../views/per/' + component + '.vue'], resolve);
} else if (component.startsWith("Sal")) {
require(['../views/sal/' + component + '.vue'], resolve);
} else if (component.startsWith("Sta")) {
require(['../views/sta/' + component + '.vue'], resolve);
} else if (component.startsWith("Sys")) {
require(['../views/sys/' + component + '.vue'], resolve);
}
//这是ES5里面的写法 []:请求组件的地址
}
退出登录时候需要清空菜单,不清空会出现问题,如下图:
管理员登录展示的菜单
登录其他用户还展示这管理员的菜单
刷新一下才回到自己拥有的菜单
所以退出登录需要清空vuex的数据
赋值一个空数组清空
在Home也上加个面包屑,方便我们去使用,ElementUI里面为我们提供了面包屑
把面包屑的代码复制到的前面,效果如下图:
这个面包屑应该是点了哪个菜单项,显示的就是哪个,所以要把面包屑的源代码改一下:
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
<!--通过this.$router.currentRoute.name:获取当前路由对象的name属性-->
<el-breadcrumb-item>{{this.$router.currentRoute.name}}</el-breadcrumb-item>
</el-breadcrumb>
修改后的效果如下图:
写完之后发现点击首页显示Home字体,不太合适,就不显示Home字体了,进行判断一下
<el-breadcrumb separator-class="el-icon-arrow-right" v-if="this.$router.currentRoute.path!='/home'">
<el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
<!--通过this.$router.currentRoute.name:获取当前路由对象的name属性-->
<el-breadcrumb-item>{{this.$router.currentRoute.name}}</el-breadcrumb-item>
</el-breadcrumb>
判断之后点击首页,就是空白的页面,空白页也不是很合适,我们可以在标签上面添加一个
<div class="homeWelcome" v-if="this.$router.currentRoute.path=='/home'">欢迎来到微人事!</div>
显示效果如下图:
很多人喜欢在登录的时候按回车进行登录,这个也是可以实现的,在Login.vue页面里面加上如下代码:
@keydown.enter.native=“submitLogin”
<el-form-item prop="password">
<el-input type="password" placeholder="请输入密码" v-model="LoginFrom.password" @keydown.enter.native="Login_submit"></el-input>
</el-form-item>