先来看看一个简单德例子
一个带按钮和计数器的简单应用程序。按下按钮会使计数器递增。虽然这很容易实现,但目标是理解基本概念。
假设应用程序中有两个组件:
1、一个按钮(事件的来源)
2、计数器(必须根据原始事件反映更新)
这两个组件彼此不了解,无法相互通信。即使是最小的Web应用程序,这也是一种非常常见的模式。在较大的应用程序中,许多组件相互通信,并且必须相互控制。以下是基本待办事项列表的一些交互:
这篇文章的目标
我们将探索解决同一问题的四种方法:
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:事件广播
让我们修改组件中的脚本。首先,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