Vuex以及几种组件通讯的使用

作者: 阿踏 | 来源:发表于2019-03-06 13:31 被阅读2次

    先来看看一个简单德例子

    target.png

    一个带按钮和计数器的简单应用程序。按下按钮会使计数器递增。虽然这很容易实现,但目标是理解基本概念。

    problem.dot.png

    假设应用程序中有两个组件:

    1、一个按钮(事件的来源)
    2、计数器(必须根据原始事件反映更新)
    这两个组件彼此不了解,无法相互通信。即使是最小的Web应用程序,这也是一种非常常见的模式。在较大的应用程序中,许多组件相互通信,并且必须相互控制。以下是基本待办事项列表的一些交互:


    todo.dot.png

    这篇文章的目标

    我们将探索解决同一问题的四种方法:
    1、使用事件广播在组件之间进行通信
    2、使用共享状态对象
    3、使用vuex

    首先,我们IncrementButton在src/components/IncrementButton.vue以下位置创建组件:

    <template>
      <button @click.prevent="activate">+1</button>
    </template>
    <script>
    export default {
      methods: {
        activate () {
          console.log('+1 Pressed')
        }
      }
    }
    </script>
    
    <style>
    </style>
    

    接下来,我们创建CounterDisplay实际显示计数器的组件。让我们创建一个新的基本vue组件src/components/CounterDisplay.vue

    <template>
      Count is {{ count }}
    </template>
    
    <script>
    export default {
      data () {
        return {
          count: 0
        }
      }
    }
    </script>
    <style>
    </style>
    

    替换App.vue为此文件:

    <template>
      <div id="app">
        <h3>Increment:</h3>
        <increment></increment>
        <h3>Counter:</h3>
        <counter></counter>
      </div>
    </template>
    
    <script>
    import Counter from './components/CounterDisplay.vue'
    import Increment from './components/IncrementButton.vue'
    export default {
      components: {
        Counter,
        Increment
      }
    }
    </script>
    
    <style>
    </style>
    

    现在,如果npm run dev再次运行,并在浏览器中打开页面,您应该会看到一个按钮和一个计数器。单击该按钮会在控制台中显示一条消息

    解决方案1:事件广播

    solution1.dot.png

    让我们修改组件中的脚本。首先,IncrementButton.vue我们使用$dispatch向父级发送一条消息,单击该按钮

    export default {
      methods: {
        activate () {
          // Send an event upwards to be picked up by App
          this.$dispatch('button-pressed')
        }
      }
    }
    

    在App.vue我们收听来自孩子的事件并重新向所有孩子广播新事件以增加。

    export default {
      components: {
        Counter,
        Increment
      },
      events: {
        'button-pressed': function () {
          // Send a message to all children
          this.$broadcast('increment')
        }
      }
    }
    >在CounterDisplay.vue我们听取increemnt事件并增加状态的价值。
    
    export default {
      data () {
        return {
          count: 0
        }
      },
      events: {
        increment () {
          this.count ++
        }
      }
    }
    

    这种方法的一些缺点:

    1、对于每个操作,父组件需要连接并将事件“分派”到正确的组件。
    2、对于更大的应用程序来说,很难理解事件的来源。
    3、业务逻辑可以在任何地方,这可能使其无法维护。

    解决方案2:共享状态

    让我们回复一下我们在解决方案1中所做的一切。我们创建一个新文件 src/store.js

    export default {
      state: {
        counter: 0
      }
    }
    

    我们先修改一下CounterDisplay.vue:

    <template>
      Count is {{ sharedState.counter }}
    </template>
    
    <script>
    import store from '../store'
    
    export default {
      data () {
        return {
          sharedState: store.state
        }
      }
    }
    export default store
    </script>
    

    我们可以修改IncrementButton.vue

    import store from '../store'
    
    export default {
      data () {
        return {
          sharedState: store.state
        }
      },
      methods: {
        activate () {
          this.sharedState.counter += 1
        }
      }
    }
    

    解决方案3:Vuex

    定义一个vuex.js文件,并在main.js里面引入

    1.在main.js里面引入vuex

    import Vuex from 'vuex'
    import vuexs from './vuex'
    Vue.use(Vuex);
    const appVuex = new Vuex.Store({
        vuexs 
    })
    new Vue({
      el: '#app',
      router,
      appVuex ,
      components: { App },
      template: '<App/>'
    })
    

    vuex.js文件代码如下

    const state = {
     count :0
    }
    const mutations={
      changeMenuIndex(state, num) {
         state.count = num
      }
     }
     const actions={
        post:function(context,payload){//这里的context和我们使用的$store拥有相同的对象和方法
           return new Promise(function(resolve, reject){
             axios.post(payload.path,payload.datas).then(data=>{
                resolve(data)
             })
           });
        }
    }
    export default {
        state,
        mutations,
        actions
    }
    

    state里面定义自己需要的变量,这里面的变量只能通过mutations,或者actions改变,以下是获取state变量方式

    1、在计算属性中返回某个状态:

    <div>{{ count }}</div>
    computed: {
        count () {
          return store.state.count
        }
      }
    

    2、mapState 辅助函数

    import {mapState} from "vuex";
    computed: {
              ...mapState(['count'])
            },
    

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

    const mutations={
      changeMenuIndex(state, num) {
         state.count = num
      }
     }
    // 在组件中使用,num为传过来的参数
    this.$store.commit('changeMenuIndex', 10);
    
    //这里有个问题就是怎样传多个参数,mutations只能定义两个参数,所以这里只能以对象的形式传值
    const mutations={
      changeMenuIndex(state, payload) {
         state.count = payload.vuexNum
      }
     }
    this.$store.commit('changeMenuIndex', {
       vuexNum: 10
    });
    //也可以这样传,type为函数名,其他的都是传参
    store.commit({
      type: 'changeMenuIndex',
      vuexNum: 10
    })
    

    这里可以使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用

    import { mapMutations } from 'vuex'
    
    export default {
      // ...
      methods: {
        // mapMutations 工具函数会将 store 中的 commit 方法映射到组件的 methods 中
        ...mapMutations([
          'changeMenuIndex', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
          // `mapMutations` 也支持载荷:
          'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
        ]),
        ...mapMutations({
          add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
        })
      }
    }
    

    Action 类似于 mutation,不同在于:
    Action 提交的是 mutation,而不是直接变更状态。
    Action 可以包含任意异步操作。

    const actions={
      //写法一
      increment (context) {
          context.commit('increment')
      }
      //写法二
      increment ({ commit }) {
         commit('changeMenuIndex')
      }
     }
    // 这里用到了对象的结构
    //因为函数的参数是一个对象,函数中用的是对象中一个方法,我们可以通过对象的
    //解构赋值直接获取到该方法
    //因为context本身就是一个对象,里面有state对象和commit方法例如
    let context {
       state: {},
       commit: function(){}
    }
    //根据对象结构可以定义如下:
    let {state,commit} = context
    console.log(state)//state: {};
    console.log(commit)//commit: function(){};
    //所以放在函数里面就是
    increment ({state,commit} ) {
         commit('changeMenuIndex')
      }
    //具体es6的用法可以参考
    `http://es6.ruanyifeng.com/`
    

    在组件中使用this.$store.dispatch('increment ',10);,这里的传参与上面讲的一样,这些都是一些常用的东西,还有一些如getter和moudle等可以看看文档
    https://vuex.vuejs.org/zh/guide/actions.html

    相关文章

      网友评论

        本文标题:Vuex以及几种组件通讯的使用

        本文链接:https://www.haomeiwen.com/subject/rkhkuqtx.html