让前端飞angular2与vue的那些事Vue.js

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

2019-03-06  本文已影响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

上一篇下一篇

猜你喜欢

热点阅读