1

vuex

vuex是一个专门为vue.js应用程序开发的状态管理模式,它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

vueX有几个核心的概念:

  • State

  • Getters

  • Mutations

  • Actions

  • Modules

首先我们用vue-cli构建一个vue目录,如图所示

clipboard.png]

其中有三个vue组件,app.vue,hello.vue,content.vue,在router目录的index.js定义了路由关系。

    import Vue from 'vue'
    import Router from 'vue-router'
    import Hello from '@/components/Hello'
    
    Vue.use(Router);
    
    
    export default new Router({
      routes: [
        {
          path: '/',
          name: 'Hello',
          component: Hello
        }
      ]
    })

APP.vue

  <template>
    <div id="app">
      <h1>{{ msg }}</h1>
      <router-view></router-view>
    </div>
  </template>
  
  <script>
  export default {
      name: 'app',
      data(){
          return {
            msg: 'Welcome to Your App.vue components'
          }
      },
  }
  </script>    

hello.vue

      <template>
        <div class="hello">
          <h1>{{ msg }}</h1>
          <contents></contents>
        </div>
      </template>
      
      <script>
      import contents from './Content';
      
      export default {
        name: 'hello',
        data () {
          return {
            msg: 'Welcome to Your Hello.vue components'
          }
        },
        components:{
          contents
        }
      }
      </script>  

content.vue

   <template>
      <div id="inner">
            <h2>{{msg}}</h2>
      </div>
   </template>
   
   
   <script>
   export default{
       name:'content',
       data(){
           return {
               msg:'Welcome to Your Content.vue components'
           }
       }
   }        
   </script>

通过上面的三个组件和路由我们可以看出,访问跟路径,app组件引入hello组件,hello.vue嵌套content.vue组件,跟main.js同级建一个store.js,定义vuex的store和简单的mutations

  import vue from 'vue';
  import vuex from 'vuex';
  
  
  vue.use(vuex);
  
  
  export default new vuex.Store({
    state:{
       time:new Data().getTime(),
       names:["张三"],
    }
  })

修改main.js,添加vuex的使用

           import Vue from 'vue'
           import App from './App'
           import router from './router'
           import store from './store.js'      
          
           new Vue({
             el: '#app',
             router,
             store,
             template: '<App/>',
             components: { App }
           });       

这个时候这个项目已经把 store 的实例注入所有的子组件,里面有一个state对象和mutations对象,state包括了全部的状态,作为一个唯一数据源存在,vue项目里面可以在任意组件里面引用state里面的属性,

如何在vue组件引入state,几种常用的办法

  1. 通过 js {{ this.$store.state.time }} 可以直接访问store定义的state对象

  2. 计算属性中返回某个状态

     computed:{
       count(){
    return this.$store.state.time;
       }
     }
  3. 当一个组件获取多个状态,上面两种使用方式比较重复和冗余,为了解决这个问题,我们可以使用mapState辅助函数

       import {mapState} from 'vuex'
       
       
       export default {
         
         computed:mapState([
      'time'
         ])
       
       }
       
  • mapState返回一个对象,映射this.count为this.$store.state.time,mapState里面参数是数组形式,参数名和state同名

    import {mapState} from 'vuex'
    
    
    computed:mapState({
       time:state=>state.time,
       nameArr:'names'
    })
     
  • 这种方式的mapState形参是一个对象,第一个count使用箭头函数,返回state.count,第二个修改了names的别名变成nameArr

Mutations

我们修改store.js,通过添加mutations,来改变store里面的state。更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutations 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数

   export default new vuex.Store({
     state:{
       time:new Date().getTime(),
       names:["张三"]
     },
     mutations:{
       pushName (state,thisName){
         state.names.push(thisName.name)
       }
     }
   })

我们修改app组件,


  <label>
        <input type="text"  v-model="addName" />
        <button @click="addNameFn">添加</button>
   </label>
  <p>姓名:
            <span v-for="name in nameArr">{{ name }} &nbsp;&nbsp;</span>
   </p>
      
    export default {
        name: 'app',
        data(){
            return {
              msg: 'Welcome to Your App.vue components',
              addName:''
            }
        },
        computed:mapState({
              nameArr:'names'
        }),
        methods:{
            addNameFn(){
                var that=this;
                this.$store.commit('pushName',{name:that.addName})               
            }
          
        }  
    }

Mutations必须是同步函数,因为每一条 mutation 被记录,devtools 都需要捕捉到前一状态和后一状态的快照。然而 mutation 在异步函数中的回调让这不可能完成
调用方式有三种

  1. 使用this.$store.commit调用,第一个参数是Mutations里面定义的方法名,第二个参数是传入的形参,建议应该是一个对象,可以包含多个字段,并且会更容易阅读代码

      addNameFn(){
     this.$store.commit('pushName',{
      name:this.addName
     })
      }
  2. 使用 type属性对象,commit调用的参数只有一个是一个对象,type是Mutations里面定义的方法名,其他字段是形参

       addNameFn(){
     this.$store.commit({
       type:'pushName',
       name:this.addName
     })
       }
  3. 使用mapMutations,引入 mapMutations ,将methods 映射为 store.commit 调用

    import {mapMutations} from 'vuex'
      
       methods:{
      ...mapMutations([
          'pushName'
      ]),
       
      addNameFn(){
        this.pushName({name:that.addName})
      }
       }

Getters

有时候我们需要根据store中的state中的属性派生一些值,例如统计添加的姓名总数,如果有多个组件需要用到统计姓名总数这个值,我们要么是在每个组件中重复写一个方法,要么是
写一个公用的方法,每个组件引入,这两种方法都不是很理想,所以引入了Getters,可以认为是store的计算属性,首先添加getters,添加一个统计添加的姓名总数的方法,并且在每个组件打印这个总数

修改store.js,添加getters属性,添加getNameLen方法,参数是state

   export default new vuex.Store({
     state:{
       time:new Date().getTime(),
       names:["张三"]
     },
     mutations:{
       pushName (state,thisName){
         state.names.push(thisName.name)
       }
     },
     getters:{
       getNameLen:state=>{
         return state.names.length
       }
     }
   })

修改App.vue的template,直接打印添加姓名的个数

  <label>
        <input type="text" v-model="addName"/>
        <button @click="addNameFn">添加</button>
  </label>
  
  <p>姓名:
        <span v-for="name in nameArr">{{ name }} &nbsp;&nbsp;</span>
  </p>
  <p>人数:{{ this.$store.getters.getNameLen }}</p>
  

修改完这些,我们发现当我们添加一个姓名,每个组件都会更新添加的姓名的总条数,getters调用就是这么简单,上面只是一种调用方式,除此外还有两种调用方式

1.在计算属性中引入

       <p>人数:{{ getNameLen }}</p>
    
    computed: {
          nameCount(){
              return  this.$store.getters.getNameLen
          }
     }
        

2.使用 mapGetters,引入mapGetters,使用个结构赋值的方法,将getters里面的方法映射到局部计算属性上,如果是数组则跟getters里面的方法名同名,对象可以修改名称,同mapState一样

    <p>人数:{{ getNameLen }}</p>
 import {mapGetters} from 'vuex'
 
 computed: {
       ...mapGetters([
         "getNameLen"
       ]),
       ...mapState({
         nameArr: 'names',
       })
  }
     

Actions

Action类似于mutation,不同在于

  • action可以包含异步操作,而mutation只能是同步的

  • mutation是直接修改state里面的数据状态,而且action是commit一个mutation来记录修改状态。

让我们通过一个简单例子来实现一个简单的action:

1.修改 config index.js,引入url模块,修改dev配置里面的proxyTable,建一个与src同级的static目录,里面放一个
getName-time0.json文件,模拟本地http请求返回的json数据,重启服务, npm run dev 访问 http://localhost:8080/dataweb/getName?time=0
就能请求到static里面的json文件


   url=require('url');

   proxyTable: {
         '/dataweb': {
           target: 'http://localhost:8080',
           changeOrigin: true,
           pathRewrite: function(path, req) {
             var urlParsed = url.parse(req.url, true),
               query = urlParsed.query,
               pathname = urlParsed.pathname.replace(/\/*$/g,'');
             pathname = pathname.substring(pathname.lastIndexOf('/'));
             Object.keys(query).forEach((key) => {
               pathname += ('-' + key + query[key]);
             });
             pathname = '/static' + pathname + '.json';
             console.log('proxy request ' + path + ' to ' + pathname);
             return pathname;
           }
         }
       }
  {
    "data":["赵钱","王五","孙刘"]
  }
  1. 创建actions,修改store.js,引入vue-resource,actions是通过提交mutations来修改状态,所以在mutations增加一个getNames方法
    state里面增加一个data字段记录数据,actions增加一个getData方法。getDate有一个默认的context参数,这个参数具有跟store实例相同的属性和方法
    通过调用context.commit提交一个mutation,或者我们可以通过es6里面的结构赋值传入一个{commit},第二个参数 _param是我们分发action传入的参数

    
    import Vue from 'vue';
    import vuex from 'vuex';
    import VueResource from 'vue-resource';
    
    Vue.use(VueResource);
    Vue.use(vuex);
    
    export default new vuex.Store({
      state:{
     time:new Date().getTime(),
     data:[],
     names:["张三"]
      },
      mutations:{
     pushName (state,thisName){
       state.names.push(thisName.name)
     },
     getNames(state,data){
       state.data=data.list;
     }
    
      },
      getters:{
     getNameLen:state=>{
       return state.names.length
     }
      },
      actions:{
      getData({ commit },_param){
        return Vue.http.get('/dataweb/getName',{
          params:_param.data
        }).then((respons)=>{
          commit({
            type:'getNames',
            list:respons.data
          });
        })
      }
      }
     })
     
  2. 修改app.vue组件,template里面增加一个按钮

     <button @click="searchName">显示全部姓名</button>
     <p><span v-for="values in nameData">{{ values }} &nbsp;&nbsp;</span></p>

data数据里面增加一个_data存储数据,methods里面增加一个searchName方法,派发getDate事件,并且传入data参数,actions的方法返回的promise,可以被store.dispatch 处理,并且store.dispatch 仍旧返回一个promise
getData里面的promise返回成功则更新this._data数据,失败则输出获取数据失败

   
    data(){
          return {
            msg: 'Welcome to Your App.vue components',
            addName: '',
            nameData:[],
          }
    },
                   
    methods: {
      searchName(){
         this.$store.dispatch({
            type:'getData',
            data:{ time:0 }
         }).then(()=>{
            this.nameData=this.$store.state.data
         },()=>{
            console.log('获取数据失败')
         })
             
      }
    
    }    

从上面的例子我们了解到,点击一个按钮发生了这些操作;

  1. 定义一个getData的action 异步调用一个请求,请求成功,commit一个getNames的mutations,修改state的data数据

  2. 执行searchName事件,用$store.dispatch派发一个getData的actions,并且传入data:{time:0}的参数

  3. dispatch返回的promise成功,就表示getDate里面的commit已经执行完毕,state.data已经有数据,更新当前this._nameData,循环输出数据

在methods派发action除了实例的这种以对象形式派发,还有三种

  • 以载荷方式

      
      this.$store.dispatch('increment',{
             data:{
               time:1
             }
      })
     
  • 在methods里面使用 mapActions 方式 映射action

    
    import {mapActions} from 'vuex';
    
      //数组形式  调用直接this.getDate
      ...mapActions([
            'getData'
       ]),
      
      //对象形式   this.searchName  等同于 this.getData
      ...mapActions({
          searchName:'getData'
       })  
            
    

    修改methods里面的searchName方法

    searchName(){
            this.getDate({
              data:{
                time:0
              }
            }).then(()=>{
              this.nameData=this.$store.state.data
            },()=>{
              console.log('数据失败')
            })
          }

vuex还有一个Modules概念,本篇文章就介绍这么多了,等学习完modules,在补充。。。。。


姜艳云
192 声望19 粉丝