目录

一、组件之间的传值方式

1.父子组件

2.非父子组件或父子组件

3.非父子组件或父子组件

二、Vuex介绍

三、Vuex安装与配置

1.下载

 2.配置

 1.在src目录下创建store模块,分别维护state/actions/mutations/getters

 2.在index.js中引入vue和vuex

3. 新建vuex的store实例

4.在main.js中导入并使用store实例

四、Vuex的简单使用

1.创建案例页面

2.往state.js中加入全局数据

3.调用state.js中的数据

常规方式:

推荐方式:

五、同步更新数据(mutations)

六、异步更新数据(actions)

1.静态异步

2.动态异步

1.准备后台数据

2.配置路径

3.action.js

4.给doDynamicAction添加事件

七、Vuex实现菜单栏折叠

1.定义一个全局数据

2.把这个数据加入到getter中

3.加到mutation中

 4.按钮与它的点击事件

5.菜单栏的收缩

6. 让菜单栏的页面也收缩


我们在上...上篇搭建首页导航与左侧菜单栏时用的是propsthis.$emit来进行组件传值的,今天我们利用vuex给他们重写一遍

一、组件之间的传值方式

1.父子组件

   父组件-->子组件,通过子组件的自定义属性:props
   子组件-->父组件,通过自定义事件:this.$emit('事件名',参数1,参数2,...);

2.非父子组件或父子组件

   通过数据总数Bus,this.$root.$emit('事件名',参数1,参数2,...)

3.非父子组件或父子组件

更好的方式是在vue中使用vuex

   方法1: 用组件之间通讯。这样写很麻烦,并且写着写着,估计自己都不知道这是啥了,很容易写晕。
   方法2: 我们定义全局变量。模块a的数据赋值给全局变量x。然后模块b获取x。这样我们就很容易获取到数据

二、Vuex介绍

官方解释:

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。可以想象为一个“前端数据库”(数据仓库),让其在各个页面上实现数据的共享包括状态,并且可操作

Vuex分成五个部分:

  1. State:单一状态树
           state为单一状态树,在state中需要定义我们所需要管理的数组、对象、字符串等等,只有在这里定义了,在vue.js的组件中才能获取你定义的这个对象的状态。
     
  2. Getters:状态获取
           getter有点类似vue.js的计算属性,当我们需要从store的state中派生出一些状态,那么我们就需要使用getter,getter会接收state作为第一个参数,而且getter的返回值会根据它的依赖被缓存起来,只有getter中的依赖值(state中的某个需要派生状态的值)发生改变的时候才会被重新计算。
     
  3. Mutations:触发同步事件
           更改store中state状态的唯一方法就是提交mutation,就很类似事件。每个mutation都有一个字符串类型的事件类型和一个回调函数,我们需要改变state的值就要在回调函数中改变。我们要执行这个回调函数,那么我们需要执行一个相应的调用方法:store.commit。
     
  4. Actions:提交mutation,可以包含异步操作
           action可以提交mutation,在action中可以执行store.commit,而且action中可以有任何的异步操作。在页面中如果我们要嗲用这个action,则需要执行store.dispatch
     
  5. Module:将vuex进行分模块
           module其实只是解决了当state中很复杂臃肿的时候,module可以将store分割成模块,每个模块中拥有自己的state、mutation、action和getter。

三、Vuex安装与配置

1.下载

npm install vuex -S

上面是默认下载vuex最新版本

需要下载指定版本只需在vuex后面加上@与版本号

例如:

npm install vuex@3.4.0 -S

 2.配置

 1.在src目录下创建store模块,分别维护state/actions/mutations/getters

 

 2.在index.js中引入vue和vuex

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

//分别导入其他相应模块(state/getters/actions/mutations)
import state from '@/store/state'
import getters from '@/store/getters'
import actions from '@/store/actions'
import mutations from '@/store/mutations'

分别导入其他相应模块state/actions/mutations/getters

3. 新建vuex的store实例

//在store/index.js文件中新建vuex的store实例,并注册上面引入的各大模块
const store = new Vuex.Store({
  state,    // 共同维护的一个状态,state里面可以是很多个全局状态
  getters,  // 获取数据并渲染
  actions,  // 数据的异步操作
  mutations,  // 处理数据的唯一途径,state的改变或赋值只能在这里
})

4.在main.js中导入并使用store实例

import store from '@/store'                //vuex
new Vue({
  el: '#app',
  router,
  store,                                   //在main.js中导入store实例
  components: { App },
  template: '<App/>'
})

四、Vuex的简单使用

1.创建案例页面

page1.vue:

<template>
  <div>
    <h1>page1-{{ts}}</h1>
  </div>
</template>

<script>
  export default{
    name:'page1',
    data:function(){
      return{
        ts:new Date().getTime()
      }
    },
    methods:{

    },
  }
</script>

<style>
</style>

page2.vue:

<template>
  <div>
    <h1>page2-{{ts}}</h1>
    <h1>{{yourname}}</h1>
  </div>
</template>

<script>
  export default{
    name:'page2',
    data:function(){
      return{
        ts:new Date().getTime()
      }
    },
    methods:{

    },
  }
</script>

<style>
</style>

2.往state.js中加入全局数据

export default{
  yourname:'名字',
}

3.调用state.js中的数据

常规方式:

//上面div中加入插值
<h1>{{yourname}}</h1>
    
//script
  export default{
    name:'page1',
    data:function(){
      return{
        ts:new Date().getTime()
      }
    },
    methods:{

    },
    computed:{
      yourname:function(){
        //不建议
        return this.$store.state.yourname;
      }
    }
  }

效果:

按照我们常规的方法就是找到state然后拿到里面的yourname属性

这个方法没错但是不建议使用

推荐方式:

 在getter.js中获取数据并渲染

export default {
  gettername: (state) => {
    return state.yourname;
  },
}

page2.vue中获取getter里面的值:

<template>
  <div>
    <h1>page2-{{ts}}</h1>
    <h1>{{gettername}}</h1>
  </div>
</template>
<script>
  export default{
    name:'page2',
    data:function(){
      return{
        ts:new Date().getTime(),
      }
    },
    methods:{
    },
    computed:{
      gettername:function(){
        return this.$store.getters.gettername;
      }
    },
  }
</script>
<style>
</style>

效果:

这种方式比上面的方法更复杂,因为它多走了一个getter,然后在获取getter里面的值,

但是还是推荐使用这种方法

五、同步更新数据(mutations)

处理数据的唯一途径,state的改变或赋值只能在这里

设置mutation.js数据

export default {
  // mutations:处理数据的唯一途径,state的改变或赋值只能在这里
  // type(事件类型): 其值为setterName
  // state:数据状态的定义
  // payload:官方给它还取了一个高大上的名字:载荷,其实就是一个保存要传递参数的容器 传递json对象
  setterName: (state, payload) => {
    state.yourname = payload.name;
  },
}

 给页面加个按钮

<button @click="doMutations">Mutations同步方法</button>
<button @click="doStaticAction">Actions异步方法</button>
<button @click="doDynamicAction">Actions动态异步方法</button>

 给doMutations添加事件:

    methods:{
      doMutations:function(){
        //错误方式:
        /* this.$store.mutations.setterName({
          name:'XXX'
        }); */

        //正确方式
        this.$store.commit('setterName',{
          name:'海害嗨',
        });
      },
    },

 效果:

六、异步更新数据(actions)

1.静态异步

    methods:{
      doMutations:function(){
        //错误方式:
        /* this.$store.mutations.setterName({
          name:'XXX'
        }); */

        //正确方式
        this.$store.commit('setterName',{
          name:'海害嗨',
        });
      },
    },

定义actions.js:

export default {
  //action:是异步数据的更改方式,本质上还是mutation来更改数据
  //payload:用于承载数据的容器,就是json
  //comtext:上下文
  setyourNameStatic: function(context, payload) {
    setTimeout(() => {
      context.commit('setterName', payload); //Action提交的是mutation
    }, 3000);
  },
}

 给doStaticAction定义事件:

    methods:{
      doMutations:function(){
        //错误方式:
        /* this.$store.mutations.setterName({
          name:'XXX'
        }); */

        //正确方式
        this.$store.commit('setterName',{
          name:'海害嗨',
        });
      },
      doStaticAction:function(){
        this.$store.dispatch('setyourNameStatic',{
          name:'来了宝贝'
        });
      },
    },

效果:

 点击按钮等三秒就出现了

2.动态异步

1.准备后台数据

public class VuexAction extends DispatcherAction {
	
	private ObjectMapper mapper=new ObjectMapper();

	public String queryVuex(HttpServletRequest req,HttpServletResponse resp)
		throws ServletException,IOException{
		
		String resturantName=req.getParameter("resturantName");
		
		SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		String date=sdf.format(new Date());
		try {
			System.out.println("模拟异步情况,睡眠6秒,不能超过10秒,axios超时时间设置的是10秒!");
			Thread.sleep(6000);
			System.out.println("睡醒了,继续...");
		} catch (Exception e) {
			e.printStackTrace();
		}
		Map<String,String> json=new HashMap<String,String>();
		json.put("msg",  resturantName+"-"+date);
		mapper.writeValue(resp.getOutputStream(),json);
		return null;
	}
}

2.配置路径

'VUEX_ALL':'/vuexAction.action',//vuex测试接口

3.action.js

export default {
  //action:是异步数据的更改方式,本质上还是mutation来更改数据
  //payload:用于承载数据的容器,就是json
  //comtext:上下文
  setyourNameStatic: function(context, payload) {
    setTimeout(() => {
      context.commit('setterName', payload); //Action提交的是mutation
    }, 3000);
  },
  setyourNameDynamic:function(context, payload) {
    //获取this
    let _this=payload._this;
    //获取请求路径
    let url=_this.axios.urls.VUEX_ALL;
    //定义请求参数
    let params={
      methodName:'queryVuex',
      resturantName:payload.name
    }
    //发起ajax请求
    _this.axios.post(url,params).then(resp=>{
      let data=resp.data;
      console.log(data);
      context.commit('setterName',{
        name:data.msg
      })
    }).catch(err=>{
      console.log(err);
    });
  },
}

4.给doDynamicAction添加事件

    methods:{
      doMutations:function(){
        //错误方式:
        /* this.$store.mutations.setterName({
          name:'XXX'
        }); */

        //正确方式
        this.$store.commit('setterName',{
          name:'海害嗨',
        });
      },
      doStaticAction:function(){
        this.$store.dispatch('setyourNameStatic',{
          name:'来了宝贝'
        });
      },
      doDynamicAction:function(){
        this.$store.dispatch('setyourNameDynamic',{
          name:'来了宝贝----02',
          _this:this
        });
      }
    },

 因为在action.js中不能使用this,所以我们把this作为参数传到action.js中

效果:

七、Vuex实现菜单栏折叠

1.定义一个全局数据

export default{
  yourname:'名字',
  collapsed:false,
}

 菜单栏的折叠是通过ture与false控制的

2.把这个数据加入到getter中

export default {
  gettername: (state) => {
    return state.yourname;
  },
  getcollapsed:(state)=>{
    return state.collapsed;
  }
}

 为了在组件中获取到state里面的数据

3.加到mutation中

export default {
  // mutations:处理数据的唯一途径,state的改变或赋值只能在这里
  // type(事件类型): 其值为setterName
  // state:数据状态的定义
  // payload:官方给它还取了一个高大上的名字:载荷,其实就是一个保存要传递参数的容器 传递json对象
  setterName: (state, payload) => {
    state.yourname = payload.name;
  },
  setcollapsed:(state)=>{
    state.collapsed=!state.collapsed;
  }
}

在点击收缩按钮的同时要把数据也改变

 4.按钮与它的点击事件

 按钮:

首页导航(TopNav.vue)中点击按钮的同时使用三元运算符改变图片

//定义一个有图片的按钮
<el-button class="buttonimg">
	<img class="showimg" :src="dcollapsed?imgshow:imgsq" @click="doToggle()">
</el-button>

 

 获取全局数据与按钮的单击事件:

<template>
	<el-menu class="el-menu-demo" mode="horizontal" background-color="#334157" text-color="#fff" active-text-color="#fff">
		<el-button class="buttonimg">
			<img class="showimg" :src="dcollapsed?imgshow:imgsq" @click="doToggle()">
		</el-button>
		<el-submenu index="2" class="submenu">
			<template slot="title">超级管理员</template>
			<el-menu-item index="2-1">设置</el-menu-item>
			<el-menu-item index="2-2">个人中心</el-menu-item>
			<el-menu-item @click="exit()" index="2-3">退出</el-menu-item>
		</el-submenu>
	</el-menu>
</template>

<script>
export default {
		TopNav:'',
    data:function(){
      return{
         /* collapsed:false, */
         imgshow:require('../assets/img/show.png'),
         imgsq:require('../assets/img/sq.png')
      }
    },
    methods:{
      exit:function(){
        this.$confirm('是否退出?', '提示', {
                  confirmButtonText: '确定',
                  cancelButtonText: '取消',
                  type: 'warning'
                }).then(() => {
                  this.$router.push("/");
                }).catch(() => {
                });
      },
      doToggle:function(){
        this.$store.commit('setcollapsed');
      }
    },
    computed:{
      dcollapsed:function(){
        return this.$store.getters.getcollapsed;
      }
    },
}
</script>

<style scoped>
	.el-menu-vertical-demo:not(.el-menu--collapse) {
		border: none;
	}
	.submenu {
		float: right;
	}
	.buttonimg {
		height: 60px;
		background-color: transparent;
		border: none;
	}
	.showimg {
		width: 26px;
		height: 26px;
		position: absolute;
		top: 17px;
		left: 17px;
	}
	.showimg:active {
		border: none;
	}
</style>

5.菜单栏的收缩

在Mian.vue中获取全局数据控制菜单栏的收缩

<template>
	<el-container class="main-container">
		<el-aside :class="dcollapsed?'main-aside-collapsed':'main-aside'">
			<LeftAside></LeftAside>
		</el-aside>
		<el-container>
			<el-header class="main-header">
				<TopNav ></TopNav>
			</el-header>
			<el-main class="main-center">
        <router-view/>
      </el-main>
		</el-container>
	</el-container>
</template>

<script>
	// 导入组件
	import TopNav from '@/components/TopNav.vue'
	import LeftAside from '@/components/LeftAside.vue'
	// 导出模块
	export default {
      name:'Main',
      data:function(){
        return{
          /* collapsed:false */
        }
      },
      components:{
        TopNav,
        LeftAside
      },
      computed:{
        dcollapsed:function(){
          return this.$store.getters.getcollapsed;
        }
      },
	};
</script>
<style scoped>
	.main-container {
		height: 100%;
		width: 100%;
		box-sizing: border-box;
	}

	.main-aside-collapsed {
		/* 在CSS中,通过对某一样式声明! important ,可以更改默认的CSS样式优先级规则,使该条样式属性声明具有最高优先级 */
		width: 64px !important;
		height: 100%;
		background-color: #334157;
		margin: 0px;
	}

	.main-aside {
		width: 240px !important;
		height: 100%;
		background-color: #334157;
		margin: 0px;
	}

	.main-header,
	.main-center {
		padding: 0px;
		border-left: 2px solid #333;
	}
  /* 加过渡给侧边导航*/
  .el-aside {
    transition: width 0.25s;
    -webkit-transition: width 0.25s;
    -moz-transition: width 0.25s;
    -webkit-transition: width 0.25s;
    -o-transition: width 0.25s;
  }
  /*加快侧边栏文字消失的速度*/
  .el-menu {
    transition: all 10ms;
  }

</style>

但是我们发现一个问题:

        他好像收缩了又好像没缩 

6. 让菜单栏的页面也收缩

找到左侧菜单栏页面LeftAside.vue

然后获取数据改变<el-menu>中的collapse属性让他实现真的收缩

<template>
	<el-menu router :default-active="$route.path" class="el-menu-vertical-demo" background-color="#334157"
	 text-color="#fff" active-text-color="#ffd04b" :collapse="dcollapsed">
		<div class="logobox">
			<img class="logoimg" src="../assets/img/logo.png" alt="">
    <el-menu-item index="/Page1">
    	<i class="el-icon-menu"></i>
    	<span slot="title">Page1</span>
    </el-menu-item>
    <el-menu-item index="/Page2">
    	<i class="el-icon-menu"></i>
    	<span slot="title">Page2</span>
    </el-menu-item>
    <el-submenu v-for="root in treenode" :index="'index-'+root.id" :key="'key-'+root.id">
      <template slot="title">
      	<i :class="root.icon"></i>
      	<span>{{root.text}}</span>
      </template>
      <el-menu-item v-for="node in root.children" :index="node.url" :key="'key-'+node.id">
      	<i :class="node.icon"></i>
      	<span slot="title">{{node.text}}</span>
      </el-menu-item>
    </el-submenu>
	</el-menu>
</template>
<script>
	export default {
      name:'LeftAside',
      data:function(){
        return{
            treenode:[],
        }
      },
      methods:{

      },
      created:function(){
        //定义请求路径
        let url=this.axios.urls.INIT_MENU;
        //发起Ajax请求
        this.axios.get(url,{
          params:{methodName:'queryTreeNode'}
        }).then(resp=>{
          let data=resp.data.data;
          console.log(data);
          this.treenode=data;
        }).catch(err=>{
          console.log(err);
        });
      },
      computed:{
        dcollapsed:function(){
          return this.$store.getters.getcollapsed;
        }
      },
	}
</script>
<style>
	.el-menu-vertical-demo:not(.el-menu--collapse) {
		width: 240px;
		min-height: 400px;
	}

	.el-menu-vertical-demo:not(.el-menu--collapse) {
		border: none;
		text-align: left;
	}

	.el-menu-item-group__title {
		padding: 0px;
	}

	.el-menu-bg {
		background-color: #1f2d3d !important;
	}

	.el-menu {
		border: none;
	}

	.logobox {
		height: 40px;
		line-height: 40px;
		color: #9d9d9d;
		font-size: 20px;
		text-align: center;
		padding: 20px 0px;
	}

	.logoimg {
		height: 40px;
	}
</style>

  博主水平有限,难免有错。欢迎评论交流

Logo

前往低代码交流专区

更多推荐