zhouwenbin/blog

vue跨组件通信的几种方法

zhouwenbin opened this issue · 8 comments

在开发组件的时候,一定会遇到组件的通信,比如点击一个图标出现弹窗和蒙层,这三个分别是不同的组件。管理他们之间的状态就成了问题。

props双向绑定

官方文档在这,通过sync双向绑定,属性变化会同步到所有组件,这也是最简单的实现方式,缺点是属性会比较多。实现方式如下
App.vue文件

<template>
  <div id="app">
    <mask :hide-mask.sync="hideMask"></mask>
    <dialog :hide-dialog.sync="hideDialog" :hide-mask.sync="hideMask"></dialog>
    <dialog-icon :hide-dialog.sync="hideDialog" :hide-mask.sync="hideMask"></dialog-icon>
  </div>
</template>

<script>
import mask from './components/mask/index'
import dialog from './components/dialog/index'
import dialogIcon from './components/dialog-icon/index'

export default {
  components: {
    mask,
    dialog,
    dialogIcon
  },
  data () {
    return {
      hideMask: true,
      hideDialog: true
    }
  }
}
</script>

component/dialog/index.vue文件

<template>
  <section class="dialog" :class="{ 'hide': hideDialog }">
    <div class="dialog-close" @click="hide()"></div>
  </section>
</template>

<script>
export default {
  props: ['hideDialog', 'hideMask'],
  methods: {
    hide () {
      this.hideDialog = !this.hideDialog
      this.hideMask = !this.hideMask
    }
  }
}
</script>

component/dialog-icon/index.vue文件

<template>
  <section class="dialog-icon" @click="show()">点击出现弹窗</section>
</template>

<script>
export default {
  props: ['hideDialog', 'hideMask'],
  methods: {
    show () {
      this.hideDialog = !this.hideDialog
      this.hideMask = !this.hideMask
    }
  }
}
</script>

component/mask/index.vue文件

<template>
  <div class="mask" :class="{ 'hide': hideMask }"></div>
</template>

<script>
export default {
  props: ['hideMask']
}
</script>

自定义事件

官方文档在这,子组件$dispatch()派发事件传递给父组件,父组件$broadcast()广播事件传递给子组件,这种方式虽然减少了props的使用,但是需要额外定义几个事件,状态多了就会变得很复杂,实现方法如下
App.vue文件

<template>
  <div id="app">
    <mask></mask>
    <dialog></dialog>
    <dialog-icon></dialog-icon>
</template>

<script>
import mask from './components/mask/index'
import dialog from './components/dialog/index'
import dialogIcon from './components/dialog-icon/index'

export default {
  components: {
    mask,
    dialog,
    dialogIcon
  },
  data () {
    return {
      hideMask: true,
      hideDialog: true
    }
  },
  events: {
    'dialog-dispatch' () {
      this.hidedialog = !this.hidedialog
      this.$broadcast('dialog-broadcast')
    },
    'mask-dispatch' () {
      this.hideMask = !this.hideMask
      this.$broadcast('mask-broadcast')
    }
  }
}
</script>

component/dialog-icon/index.vue文件

<template>
  <section class="dialog-icon" @click="show()">点击出现弹窗</section>
</template>

<script>
export default {
  methods: {
    show () {
      this.$dispatch('dialog-dispatch')
      this.$dispatch('mask-dispatch')
    }
  },
  events: {
    'dialog-broadcast' () {
      this.hideDialog = !this.hideDialog
    }
  },
  data () {
    return {
      hideDialog: this.$parent.hideDialog,
      hideMask: this.$parent.hideMask
    }
  }
}
</script>

component/dialog/index.vue文件

<template>
  <section class="dialog" :class="{ 'hide': hideDialog }">
    <div class="dialog-close" @click="hide()"></div>
  </section>
</template>

<script>
export default {
  methods: {
    hide () {
      this.$dispatch('dialog-dispatch')
      this.$dispatch('mask-dispatch')
    }
  },
  events: {
    'dialog-broadcast' () {
      this.hideDialog = !this.hideDialog
    }
  },
  data () {
    return {
      hideDialog: this.$parent.hideDialog,
      hideMask: this.$parent.hideMask
    }
  }
}
</script>

component/mask/index.vue文件

<template>
  <div class="mask" :class="{ 'hide': hideMask }"></div>
</template>

<script>
export default {
  data () {
    return {
      hideMask: this.$parent.hideMask
    }
  },
  events: {
    'mask-broadcast' () {
      this.hideMask = !this.hideMask
    }
  }
}
</script>

Vuex

官方文档在这里,状态统一放store管理,修改状态通过mutations,组件通过action调用mutations,虽然有点绕,但是所有东西放一起后期会更好维护,实现方法如下
App.vue文件

<template>
  <div id="app">
    <mask></mask>
    <dialog></dialog>
    <dialog-icon></dialog-icon>
  </div>
</template>

<script>
import mask from './components/mask/index'
import dialog from './components/dialog/index'
import dialogIcon from './components/dialog-icon/index'

export default {
  components: {
    mask,
    dialog,
    dialogIcon
  }
}
</script>

component/dialog/index.vue文件

<template>
  <section class="storehouse dialog" :class="{ 'hide': isHideDialog }">
    <div class="dialog-close" @click="hideDialog()"></div>
  </section>
</template>

<script>
import { hideDialog } from '../../vuex/actions'

export default {
  vuex: {
    state: {
      isHideDialog: state => state.isHideDialog
    },
    actions: {
      hideDialog
    }
  }
}
</script>

component/dialog-icon/index.vue文件

<template>
  <section class="storehouse-icon" @click="hideDialog()">点击出现弹窗</section>
</template>

<script>
import { hideDialog } from '../../vuex/actions'

export default {
  vuex: {
    actions: {
      hideDialog
    }
  }
}
</script>

component/mask/index.vue文件

<template>
  <div class="mask" :class="{ 'hide': isHideMask }"></div>
</template>

<script>
export default {
  vuex: {
    state: {
      isHideMask: state => state.isHideMask
    }
  }
}
</script>

vuex/store.js文件

import Vue from 'vue'
import Vuex from 'vuex'
import mutations from './mutations'

Vue.use(Vuex)

const state = {
  isHideMask: true,
  isHideDialog: true
}

const store = new Vuex.Store({
  state,
  mutations
})

if (module.hot) {
  module.hot.accept(['./mutations'], () => {
    const mutations = require('./mutations').default
    store.hotUpdate({
      mutations
    })
  })
}

export default store

vuex/mutations.js文件

import {
  HIDEDIALOG
}
from './mutation-types'

export
default {
  [HIDEDIALOG] (state) {
    state.isHideDialog = !state.isHideDialog
    state.isHideMask = !state.isHideMask
  }
}

vuex/mutations-types.js文件

export const HIDEDIALOG = 'HIDEDIALOG'

vuex/action.js文件

import { HIDEDIALOG } from './mutation-types'

export const hideDialog = ({ dispatch }) => dispatch(HIDEDIALOG)

正好学习的时候看到,言简意赅,非常感谢~

mark

🐶 🐶 🐶 🐶 🐶

我用你的vuex,报错,我也不知道为什么

@15831929073 文章是1.0版本的

66666

mark