0
点赞
收藏
分享

微信扫一扫

Vue组件通信秘籍:掌握这10大绝招,轻松玩转组件间数据传递

朱小落 03-15 22:30 阅读 2

1. props / $emit

父组件

<template>
  <child-component :msg="parentMsg" @child-event="handleChildEvent"></child-component>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  },
  data() {
    return {
      parentMsg: 'Message from parent'
    };
  },
  methods: {
    handleChildEvent(data) {
      console.log('Received data from child:', data);
    }
  }
};
</script>

子组件

<template>
  <button @click="sendToParent">Send to Parent</button>
</template>

<script>
export default {
  props: ['msg'],
  methods: {
    sendToParent() {
      this.$emit('child-event', 'Data from child');
    }
  }
};
</script>

2. $attrs / $listeners

祖先组件

<template>
  <great-grandchild-component :foo="foo" :bar="bar" @baz="handleBaz"></great-grandchild-component>
</template>

<script>
import GreatGrandchildComponent from './GreatGrandchildComponent.vue';

export default {
  components: {
    GreatGrandchildComponent
  },
  data() {
    return {
      foo: 'Foo',
      bar: 'Bar'
    };
  },
  methods: {
    handleBaz() {
      console.log('Baz event received');
    }
  }
};
</script>

中间组件

<template>
  <grandchild-component v-bind="$attrs" v-on="$listeners"></grandchild-component>
</template>

<script>
import GrandchildComponent from './GrandchildComponent.vue';

export default {
  components: {
    GrandchildComponent
  }
};
</script>

最终子组件

<template>
  <div>{{ foo }} {{ bar }}</div>
  <button @click="$emit('baz')">Emit Baz</button>
</template>

<script>
export default {
  props: ['foo', 'bar']
};
</script>

3. 父链 / 子链($parent / $children)

在 Vue.js 中,$parent$children 属性可以用于直接访问组件的父级和子级实例,从而进行组件间的通信。然而,这种方法并不推荐用于常规的组件通信,因为它破坏了组件的封装性,使得组件之间的耦合度过高。但在某些特定场景下,如果你确实需要使用这种方法,下面是一个简单的示例:

父组件

<template>
  <div>
    <h2>Parent Component</h2>
    <child-component></child-component>
    <button @click="sendMessageToChild">Send Message to Child</button>
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  },
  methods: {
    sendMessageToChild() {
      // 通过 $children 访问子组件并调用其方法
      this.$children.forEach(child => {
        if (child.$options.name === 'ChildComponent') {
          child.receiveMessage('Hello from Parent!');
        }
      });
    }
  }
};
</script>

子组件

<template>
  <div>
    <h3>Child Component</h3>
    <p>{{ messageFromParent }}</p>
  </div>
</template>

<script>
export default {
  name: 'ChildComponent',
  data() {
    return {
      messageFromParent: ''
    };
  },
  methods: {
    receiveMessage(message) {
      this.messageFromParent = message;
    }
  }
};
</script>

4. provide / inject

祖先组件

<template>
  <div>
    <child-component></child-component>
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  },
  provide() {
    return {
      theme: 'dark'
    };
  }
};
</script>

子组件

<template>
  <div :class="theme">Child Component</div>
</template>

<script>
export default {
  inject: ['theme']
};
</script>

5. Vuex

Store

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++;
    }
  }
});

组件中使用

<template>
  <button @click="increment">Increment</button>
  <p>Count: {{ count }}</p>
</template>

<script>
import { mapState, mapMutations } from 'vuex';

export default {
  computed: {
    ...mapState(['count'])
  },
  methods: {
    ...mapMutations(['increment'])
  }
};
</script>

6. Event Bus

Event Bus

import Vue from 'vue';
export const EventBus = new Vue();

发送事件的组件

<template>
  <button @click="sendEvent">Send Event</button>
</template>

<script>
import { EventBus } from './event-bus';

export default {
  methods: {
    sendEvent() {
      EventBus.$emit('custom-event', 'Data to send');
    }
  }
};
</script>

接收事件的组件

<template>
  <div>Listening for events...</div>
</template>

<script>
import { EventBus } from './event-bus';

export default {
  created() {
    EventBus.$on('custom-event', this.handleCustomEvent);
  },
  beforeDestroy() {
    EventBus.$off('custom-event', this.handleCustomEvent);
  },
  methods: {
    handleCustomEvent(data) {
      console.log('Received data:', data);
    }
  }
};
</script>

7. Slot 插槽

父组件

<template>
  <child-component>
    <div slot="header">Header Content</div>
    <div slot="footer">Footer Content</div>
  </child-component>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  }
};
</script>

子组件

<template>
  <div>
    <slot name="header"></slot>
    <main>Main Content</main>
    <slot name="footer"></slot>
  </div>
</template>

<script>
export default {
  // ...
};
</script>

8. ref

父组件

<template>
  <child-component ref="childRef"></child-component>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  },
  mounted() {
    console.log(this.$refs.childRef); // 访问子组件实例
  }
};
</script>

子组件

<template>
  <div>Child Component</div>
</template>

<script>
export default {
  // ...
};
</script>

9. provide / inject 的替代方案:使用 context

虽然 provideinject 已经是 Vue 的内置特性,但如果你想要通过 context 来实现类似的功能,可以考虑使用 Vue 的混入(mixins)。

Mixin

export const InjectionMixin = {
  created() {
    if (this.$options.contextProvided) {
      this.$context = this.$options.contextProvided;
    }
  }
};

提供 context 的组件

<template>
  <div>
    <child-component></child-component>
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue';
import InjectionMixin from './InjectionMixin';

export default {
  components: {
    ChildComponent
  },
  mixins: [InjectionMixin],
  data() {
    return {
      contextData: 'Some data'
    };
  },
  provide() {
    return {
      contextProvided: this
    };
  }
};
</script>

接收 context 的组件

<template>
  <div>{{ contextData }}</div>
</template>

<script>
import InjectionMixin from './InjectionMixin';

export default {
  mixins: [InjectionMixin],
  computed: {
    contextData() {
      return this.$context.contextData;
    }
  }
};
</script>

10. 全局混入实现组件通信

全局混入

Vue.mixin({
  created() {
    const eventBus = this.$root.$eventBus = new Vue();
    Object.defineProperty(this, '$eventBus', {
      get() {
        return eventBus;
      }
    });
  }
});

发送事件的组件

<template>
  <button @click="sendEvent">Send Event</button>
</template>

<script>
export default {
  methods: {
    sendEvent() {
      this.$eventBus.$emit('global-event', 'Data to send');
    }
  }
};
</script>

接收事件的组件

<template>
  <div>Listening for global events...</div>
</template>

<script>
export default {
  created() {
    this.$eventBus.$on('global-event', this.handleGlobalEvent);
  },
  beforeDestroy() {
    this.$eventBus.$off('global-event', this.handleGlobalEvent);
  },
  methods: {
    handleGlobalEvent(data) {
      console.log('Received global data:', data);
    }
  }
};
</script>

10. Vuex 状态管理

Vuex 是 Vue.js 的状态管理模式和库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

安装 Vuex

npm install vuex --save

创建 Vuex Store

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++;
    }
  },
  actions: {
    incrementAction({ commit }) {
      commit('increment');
    }
  }
});

在 Vue 应用中使用 Vuex

import Vue from 'vue';
import App from './App.vue';
import store from './store';

new Vue({
  store,
  render: h => h(App)
}).$mount('#app');

组件中使用 Vuex

<template>
  <div>
    <p>{{ count }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

<script>
export default {
  computed: {
    count() {
      return this.$store.state.count;
    }
  },
  methods: {
    increment() {
      this.$store.dispatch('incrementAction');
    }
  }
};
</script>

这些是 Vue.js 中实现组件通信的一些常见方法。选择哪种方法取决于你的具体需求和项目的结构。在大型项目中,Vuex 通常是一个很好的选择,因为它提供了一个结构化的方式来管理状态。在小型项目中,你可能只需要使用 props 和 events,或者 event bus 就足够了。

举报

相关推荐

0 条评论