Skip to content

vue跨组件通信的几种方法 #20

Open
@zhouwenbin

Description

@zhouwenbin
Owner

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

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)

Activity

gaoryrt

gaoryrt commented on Aug 9, 2016

@gaoryrt

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

jiangtao

jiangtao commented on Oct 7, 2016

@jiangtao

mark

RunningV

RunningV commented on Dec 9, 2016

@RunningV

🐶 🐶 🐶 🐶 🐶

zhangwei900808

zhangwei900808 commented on Feb 19, 2017

@zhangwei900808

mark

old-zsh

old-zsh commented on Feb 27, 2017

@old-zsh

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

zhouwenbin

zhouwenbin commented on Feb 27, 2017

@zhouwenbin
OwnerAuthor

@15831929073 文章是1.0版本的

zycxwd

zycxwd commented on Apr 18, 2017

@zycxwd

66666

xuxuechao6

xuxuechao6 commented on Jun 20, 2018

@xuxuechao6

mark

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @zhouwenbin@jiangtao@zhangwei900808@gaoryrt@RunningV

        Issue actions

          vue跨组件通信的几种方法 · Issue #20 · zhouwenbin/blog