Vue 3中的13种通讯方式

1. Props:父组件向子组件传递数据

当父组件需要向子组件传递数据时,可以使用 props 来实现。props 是父组件传递给子组件的属性,子组件可以通过这些属性来接收数据。

以下是一个简单的示例,演示了如何在 Vue 组件中使用 props:


<template>
  <div>
    <child-component :message="parentMessage"></child-component>
  </div>
</template>

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

export default {
  components: {
    ChildComponent
  },
  data() {
    return {
      parentMessage: 'Hello from parent'
    };
  }
};
</script>
在这个示例中,parentMessage 是父组件中的一个数据,我们将它通过 :message 传递给了子组件 ChildComponent。在子组件中,可以通过 props 接收并使用这个数据:


<template>
  <div>
    <p>{{ message }}</p>
  </div>
</template>

<script>
export default {
  props: ['message']
};
</script>
现在,子组件会显示来自父组件的消息。

使用 props 时需要注意的几点:

props 数据是单向的,即父组件传递给子组件,子组件不能直接修改 props 的值。
props 可以传递任何类型的数据,包括字符串、数字、对象、数组等。
如果需要在子组件中修改 props,通常应该在子组件内部定义一个局部的数据属性,将 props 的值复制给这个属性,然后对这个属性进行修改。
在子组件中,可以通过 this.message 来访问 props 中的数据。
在 Vue 3 中,setup 函数中使用 props 的方式有所不同,你可以查阅 Vue 3 的官方文档获取更多信息。


2. $emit():子组件向父组件发送事件

当子组件需要向父组件发送事件时,可以使用 $emit() 方法。这个方法允许子组件触发一个自定义事件,并将数据传递给父组件。

以下是一个简单的示例,演示了如何在 Vue 组件中使用 $emit() 来发送事件:

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

<script>
export default {
  methods: {
    sendEvent() {
      // 使用 $emit 发送自定义事件,第一个参数是事件名称,第二个参数是要传递的数据
      this.$emit('custom-event', 'Hello from child');
    }
  }
};
</script>
在这个示例中,子组件中的 sendEvent 方法使用 $emit 发送了一个名为 'custom-event' 的自定义事件,并传递了数据 'Hello from child'。

在父组件中,可以监听这个自定义事件,并在事件处理函数中获取传递的数据:


<template>
  <div>
    <child-component @custom-event="handleEvent"></child-component>
    <p>{{ messageFromChild }}</p>
  </div>
</template>

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

export default {
  components: {
    ChildComponent
  },
  data() {
    return {
      messageFromChild: ''
    };
  },
  methods: {
    handleEvent(data) {
      // 在事件处理函数中获取传递的数据
      this.messageFromChild = data;
    }
  }
};
</script>
在父组件中,使用 @custom-event 监听子组件发送的自定义事件,并在 handleEvent 方法中获取传递的数据。

使用 $emit() 时需要注意的几点:

第一个参数是事件名称,可以自定义,但要确保它在父组件中是唯一的。
第二个参数是要传递给父组件的数据。
父组件通过 @custom-event 来监听子组件发送的事件,并在相应的处理函数中获取数据。
在事件处理函数中,可以将子组件发送的数据赋值给父组件的属性,从而在父组件中显示。


3. $refs:使用ref属性获取子组件的实例或DOM元素

$refs 是 Vue 提供的一个属性,用于在父组件中获取子组件的实例或 DOM 元素。这可以用于直接访问子组件的属性和方法,或者操作子组件的 DOM 元素。

以下是一个使用 $refs 的示例:


<template>
  <div>
    <child-component ref="childRef"></child-component>
    <button @click="callChildMethod">Call Child Method</button>
  </div>
</template>

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

export default {
  components: {
    ChildComponent
  },
  methods: {
    callChildMethod() {
      // 通过 $refs 获取子组件实例,然后调用子组件的方法
      this.$refs.childRef.childMethod();
    }
  }
};
</script>
在这个示例中,父组件通过 ref="childRef" 将子组件的实例存储在 $refs 中。然后,在 callChildMethod 方法中,通过 $refs.childRef 获取子组件的实例,并调用了子组件的 childMethod 方法。

需要注意的是,使用 $refs 可能会有一些限制和潜在的问题:

不推荐在模板中使用 $refs,因为在模板编译完成之后才会填充 $refs。
$refs 不是响应式的,因此不适合用于数据的绑定和观察。
如果使用 v-if 或 v-for 条件渲染子组件,$refs 可能无法访问到正确的子组件实例。
如果组件在 <keep-alive> 内部,$refs 可能无法访问缓存的实例。
总之,在使用 $refs 时需要注意它的限制,并且尽量避免在模板中直接使用,而是在方法中进行处理。





4. provide/inject:祖先组件向后代组件传递数据

provide 和 inject 是 Vue 中另一种用于组件通信的方法。它允许祖先组件向后代组件传递数据,甚至可以在嵌套层级较深的组件中传递数据。

provide:在祖先组件中使用 provide 提供数据,子孙组件可以通过 inject 来接收这些数据。

inject:在子孙组件中使用 inject 来注入祖先组件提供的数据。

以下是一个简单的示例:


<!-- AncestorComponent.vue -->
<template>
  <div>
    <child-component></child-component>
  </div>
</template>

<script>
export default {
  provide: {
    message: 'Hello from AncestorComponent'
  }
};
</script>


<!-- ChildComponent.vue -->
<template>
  <div>
    <grandchild-component></grandchild-component>
  </div>
</template>

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

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

<!-- GrandchildComponent.vue -->
<template>
  <div>
    <p>{{ message }}</p>
  </div>
</template>

<script>
export default {
  inject: ['message']
};
</script>
在这个示例中,AncestorComponent 使用 provide 提供了一个名为 message 的数据,然后 ChildComponent 和 GrandchildComponent 都使用 inject 来接收这个数据,并在 GrandchildComponent 中显示出来。

优点:

数据在多层级组件中传递更加方便,不需要通过多层级的 props 传递。
提供了更好的封装,子孙组件不需要知道数据来自哪个祖先组件。
缺点:

使用 inject 会导致组件在依赖祖先组件的情况下变得不再具有独立性,降低了复用性。
inject 不是响应式的,所以在数据更新时可能需要手动更新视图。
需要注意的是,provide 和 inject 本质上是一种高级的用法,可能会增加组件之间的耦合性。在使用时,需要慎重考虑组件之间的关系和数据的传递方式。


5. $attrs/$listeners:父组件向子组件传递非props数据与事件

$attrs 和 $listeners 是 Vue 中用于父组件向子组件传递非 prop 数据和事件的方式。它们通常用于实现透传,即将父组件中的特性和事件传递给子组件。

$attrs:用于传递父组件中没有被子组件所声明(也就是没有被定义为 prop)的特性。

$listeners:用于传递父组件中的所有事件监听器。

以下是一个示例:

<!-- ParentComponent.vue -->
<template>
  <div>
    <child-component message="Hello" v-on:click="handleClick"></child-component>
  </div>
</template>

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

export default {
  components: {
    ChildComponent
  },
  methods: {
    handleClick() {
      console.log('Clicked in ParentComponent');
    }
  }
};
</script>

<!-- ChildComponent.vue -->
<template>
  <div>
    <grandchild-component v-bind="$attrs" v-on="$listeners"></grandchild-component>
  </div>
</template>

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

export default {
  components: {
    GrandchildComponent
  }
};
</script>
vue
Copy code
<!-- GrandchildComponent.vue -->
<template>
  <div>
    <button v-bind="$attrs" v-on="$listeners">{{ message }}</button>
  </div>
</template>

<script>
export default {
  inheritAttrs: false,
  props: ['message']
};
</script>
在这个示例中,ParentComponent 向 ChildComponent 传递了一个特性 message 和一个事件监听器 click。然后,ChildComponent 使用 v-bind="$attrs" 和 v-on="$listeners" 将这些特性和事件传递给了 GrandchildComponent,从而实现了特性和事件的透传。

优点:

父组件可以将自己的特性和事件直接传递给子组件,不需要在子组件中重新声明。
提供了一种简洁的方式来实现透传,避免了重复编写相似的代码。
缺点:

在子组件中使用 $attrs 和 $listeners 可能会让组件之间的关系变得复杂,难以追踪数据流动。
在子组件中使用 $attrs 和 $listeners 时需要注意一些坑,比如事件重命名、特性的影响等。
总之,$attrs 和 $listeners 提供了一种便捷的方式来在父子组件之间传递非 prop 数据和事件,但在使用时需要谨慎考虑,确保不会影响到组件的维护和可读性。





6. EventBus:使用Vue实例作为事件总线,在组件之间传递数据

EventBus 是 Vue 中的一种事件总线模式,它允许你创建一个中央化的事件总线实例,用于在不同组件之间传递事件和数据。

以下是如何使用 EventBus 的一个简单示例:


// EventBus.js
import Vue from 'vue';

const EventBus = new Vue();

export default EventBus;
在这个示例中,我们创建了一个名为 EventBus 的新 Vue 实例,它可以用来传递事件和数据。

然后,在需要传递事件的组件中,你可以使用 EventBus 来触发和监听事件:


// ComponentA.vue
<template>
  <div>
    <button @click="sendMessage">Send Message</button>
  </div>
</template>

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

export default {
  methods: {
    sendMessage() {
      EventBus.$emit('messageSent', 'Hello from ComponentA');
    }
  }
};
</script>

// ComponentB.vue
<template>
  <div>
    <p>{{ receivedMessage }}</p>
  </div>
</template>

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

export default {
  data() {
    return {
      receivedMessage: ''
    };
  },
  created() {
    EventBus.$on('messageSent', message => {
      this.receivedMessage = message;
    });
  }
};
</script>
在这个示例中,ComponentA 使用 EventBus.$emit 发送了一个名为 messageSent 的事件,而 ComponentB 使用 EventBus.$on 来监听该事件并更新显示的消息。

优点:

简单明了,不需要在父子组件之间传递多层嵌套的 props。
可以实现非父子关系的任意两个组件之间的通信。
缺点:

可能会导致事件的跟踪和管理变得困难,因为事件的触发和监听可以发生在任意地方。
在大型应用中,使用全局的事件总线可能会导致事件命名冲突和不易维护的情况。
总之,EventBus 是一种方便的方式来在不同组件之间进行通信,但在使用时需要谨慎考虑,以确保代码的可读性和维护性。


7. Vuex:用于管理大型应用程序中的状态管理

当涉及到 Vuex 的实际代码时,这里我可以为你提供一个简单的示例来说明如何在 Vue 应用中使用 Vuex 进行状态管理。

假设你有一个需要管理用户登录状态的应用。以下是一个使用 Vuex 的基本示例:

安装 Vuex:
首先,在你的 Vue 项目中安装 Vuex。
bash

npm install vuex --save
创建 Vuex Store:
在项目中创建一个 Vuex Store,它将管理你的应用状态。假设你的应用需要管理用户的登录状态。

javascript

// store.js
import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    loggedIn: false,
    user: null,
  },
  mutations: {
    login(state, user) {
      state.loggedIn = true;
      state.user = user;
    },
    logout(state) {
      state.loggedIn = false;
      state.user = null;
    },
  },
  actions: {
    loginUser({ commit }, user) {
      // Simulate an API call
      setTimeout(() => {
        commit('login', user);
      }, 1000);
    },
    logoutUser({ commit }) {
      commit('logout');
    },
  },
  getters: {
    isLoggedIn: state => state.loggedIn,
    currentUser: state => state.user,
  },
});

export default store;
在主应用中注册 Vuex Store:
在你的 Vue 主应用中,注册创建的 Vuex Store。

javascript

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

new Vue({
  render: h => h(App),
  store, // 注册 Vuex Store
}).$mount('#app');
在组件中使用 Vuex 的状态和方法:
现在你可以在你的组件中使用 Vuex 的状态和方法了。


<template>
  <div>
    <h1>My App</h1>
    <p v-if="isLoggedIn">Logged in as {{ currentUser }}</p>
    <button @click="login">Login</button>
    <button @click="logout">Logout</button>
  </div>
</template>

<script>
import { mapGetters, mapActions } from 'vuex';

export default {
  computed: {
    ...mapGetters(['isLoggedIn', 'currentUser']),
  },
  methods: {
    ...mapActions(['loginUser', 'logoutUser']),
    login() {
      this.loginUser('john_doe'); // 调用 Vuex action
    },
    logout() {
      this.logoutUser(); // 调用 Vuex action
    },
  },
};
</script>
这个简单的示例展示了如何创建一个简单的 Vuex Store,并在组件中使用 Vuex 的状态和方法。在实际应用中,你可以根据需求进行更复杂的状态管理和业务逻辑处理。


8. $parent/$children:通过父/子组件实例的引用进行通讯

通过 $parent 和 $children 可以在 Vue 组件中进行父子组件之间的通信。这两个属性分别允许你在组件内部访问父组件和子组件的实例,从而实现数据和方法的传递。

这里是一个简单的示例,展示了如何使用 $parent 和 $children 进行通信:

假设你有一个父组件 ParentComponent 和一个子组件 ChildComponent,你想要在子组件中调用父组件的方法并访问父组件的数据。


<template>
  <div>
    <h1>Parent Component</h1>
    <p>Count: {{ count }}</p>
    <button @click="incrementCount">Increment Count</button>
    <ChildComponent></ChildComponent>
  </div>
</template>

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

export default {
  components: {
    ChildComponent,
  },
  data() {
    return {
      count: 0,
    };
  },
  methods: {
    incrementCount() {
      this.count++;
    },
  },
};
</script>

<!-- ChildComponent.vue -->
<template>
  <div>
    <h2>Child Component</h2>
    <button @click="callParentMethod">Call Parent Method</button>
  </div>
</template>

<script>
export default {
  methods: {
    callParentMethod() {
      // 通过 $parent 访问父组件的实例并调用方法
      this.$parent.incrementCount();
    },
  },
};
</script>
在这个示例中,子组件通过 $parent 访问了父组件的实例,并调用了父组件的 incrementCount 方法,从而更新了父组件中的 count 数据。

尽管 $parent 和 $children 可以用来在组件之间传递数据和调用方法,但这种方式并不是最优雅和推荐的通信方式,因为它们会在组件结构发生变化时变得脆弱。更好的做法是使用 props 和 $emit 实现父子组件之间的通信,或者使用 Vuex 进行状态管理。


9. provide/inject与$attrs/$listeners配合使用:更复杂的祖先/后代之间的通讯方式

provide 和 inject 是用于在 Vue 组件树中进行祖先组件向后代组件传递数据的高级通信方式。它们通常在复杂的组件嵌套结构中使用,以避免多层级嵌套时的繁琐的数据传递。

这里是一个示例,演示了如何在祖先组件中使用 provide 提供数据,然后在后代组件中使用 inject 来接收这些数据,并配合 $attrs 和 $listeners 实现更复杂的通信。


<template>
  <div>
    <h1>Grandparent Component</h1>
    <p>Grandparent Data: {{ grandparentData }}</p>
    <ParentComponent></ParentComponent>
  </div>
</template>

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

export default {
  components: {
    ParentComponent,
  },
  provide() {
    return {
      grandparentData: 'Data from Grandparent',
    };
  },
};
</script>

<!-- ParentComponent.vue -->
<template>
  <div>
    <h2>Parent Component</h2>
    <p>Parent Data: {{ parentData }}</p>
    <ChildComponent></ChildComponent>
  </div>
</template>

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

export default {
  components: {
    ChildComponent,
  },
  inject: ['grandparentData'], // 接收祖先组件提供的数据
  data() {
    return {
      parentData: 'Data from Parent',
    };
  },
};
</script>

<!-- ChildComponent.vue -->
<template>
  <div>
    <h3>Child Component</h3>
    <p>Child Data: {{ childData }}</p>
    <p>Grandparent Data via inject: {{ grandparentData }}</p>
    <p v-bind="$attrs">Other Attributes from Parent</p>
    <button v-on="$listeners">Click Me</button>
  </div>
</template>

<script>
export default {
  inject: ['grandparentData'], // 接收祖先组件提供的数据
  props: ['childData'],
};
</script>
在这个示例中,provide 和 inject 配合使用,允许 GrandparentComponent 向其后代组件传递数据。在 ChildComponent 中,通过 inject 可以接收来自祖先组件的数据 grandparentData。此外,使用 $attrs 可以传递来自父组件的其他属性,而使用 $listeners 可以将父组件的事件监听器传递给子组件。

请注意,$attrs 和 $listeners 只在子组件的根元素上有效,而不会传递给子组件中的子组件。这种方式适用于需要在组件层次结构中传递属性和事件的情况,但在复杂通信需求时,更推荐使用 Vuex 或事件总线等更高级的通信方式。


10. Teleport:在DOM树中任意位置渲染组件

Teleport 是 Vue 3 中新增的特性,用于在组件的模板中将内容渲染到 DOM 树中的任意位置,而不受组件层次结构的限制。它适用于需要在组件之外渲染内容的场景,比如弹出框、对话框等。

以下是一个示例,演示如何使用 Teleport 渲染一个弹出框:


<template>
  <div>
    <button @click="showModal = true">Show Modal</button>
    <Teleport to="body">
      <Modal v-if="showModal" @close="showModal = false">
        <!-- Modal Content -->
      </Modal>
    </Teleport>
  </div>
</template>

<script>
import { ref } from 'vue';
import Modal from './Modal.vue';

export default {
  components: {
    Modal,
  },
  setup() {
    const showModal = ref(false);

    return {
      showModal,
    };
  },
};
</script>
在这个示例中,当点击 "Show Modal" 按钮时,Teleport 将 <Modal> 组件的内容渲染到 <body> 元素下,而不受组件层次结构的影响。这样可以确保弹出框的样式和行为在不同层次的组件中都是一致的。

需要注意的是,Teleport 在渲染内容时仍然会受到 CSS 层叠上下文和层次结构的影响,因此需要在使用时注意样式的调整。

总结起来,Teleport 是一个很有用的特性,可以方便地将组件的内容渲染到任意位置,增强了 Vue 在处理 UI 布局和渲染方面的灵活性。


11. Composition API:使用setup()函数和reactive()函数等新的API编写组件

Composition API 是 Vue 3 中引入的一组新的 API,旨在更好地组织和复用代码,特别是在处理复杂逻辑的组件时。它通过 setup() 函数和一些新的函数(如 reactive()、computed()、watch() 等)来编写组件。

下面是一个简单的示例,演示了如何使用 Composition API 编写一个基本的计数器组件:


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

<script>
import { ref } from 'vue';

export default {
  setup() {
    // 创建一个响应式变量
    const count = ref(0);

    // 定义一个函数来增加计数
    const increment = () => {
      count.value++;
    };

    // 返回响应式数据和函数
    return {
      count,
      increment,
    };
  },
};
</script>
在这个示例中,使用 ref() 函数来创建一个响应式变量 count,然后定义了一个 increment 函数来增加计数。通过 setup() 函数返回 count 和 increment,使它们在模板中可以被访问和使用。

Composition API 还引入了其他一些新的函数,例如 reactive() 用于创建响应式对象,computed() 用于创建计算属性,watch() 用于监听响应式数据的变化等等。这些 API 可以帮助你更灵活、更清晰地组织和管理组件中的逻辑。

总体而言,Composition API 是 Vue 3 中一个非常强大的功能,使得编写组件变得更加灵活、可维护和可测试。它特别适合处理复杂的业务逻辑和状态管理。


12. Directive:自定义指令,用于操作DOM元素

Vue 提供了自定义指令的机制,允许你在 DOM 元素上添加自定义行为。自定义指令可以用于操作 DOM、添加事件监听器、修改元素样式等等。

下面是一个简单的示例,展示如何创建一个自定义指令来实现双击文本选中功能:

<template>
  <div>
    <p v-selectable>Double click to select this text.</p>
  </div>
</template>

<script>
// 注册一个全局自定义指令 `v-selectable`
app.directive('selectable', {
  // 当绑定元素插入到 DOM 中时
  mounted(el) {
    // 添加双击事件监听
    el.addEventListener('dblclick', () => {
      // 选中文本
      window.getSelection().selectAllChildren(el);
    });
  }
});

export default {
  name: 'CustomDirectiveExample',
};
</script>
在这个示例中,我们使用 app.directive 来注册一个名为 selectable 的全局自定义指令。在 mounted 钩子中,我们为元素添加了双击事件监听,当元素被双击时,会选中元素中的文本内容。

通过使用自定义指令,你可以在 Vue 中实现各种自定义的 DOM 操作和交互行为,从而更好地控制页面的交互和展示。

需要注意的是,自定义指令在 Vue 3 的 Composition API 中也得到了改进,可以更灵活地定义和使用。


13. Component Events:使用Vue.extend()创建一个可重用的组件,可以通过props和emit实现组件之间的通讯。

Vue 中是一种非常常见的组件之间通信的方法。通过 Vue.extend() 创建的可重用组件可以通过 props 接收父组件传递的数据,并通过 this.$emit() 发送事件给父组件。

以下是一个简单示例,演示如何通过 props 和 emit 在父子组件之间进行通信:

首先,我们有一个可重用的子组件 ChildComponent:


<template>
  <div>
    <p>Child Component</p>
    <button @click="sendMessage">Send Message to Parent</button>
  </div>
</template>

<script>
export default {
  methods: {
    sendMessage() {
      this.$emit('message-from-child', 'Hello from child!');
    }
  }
};
</script>
然后,在父组件中使用这个子组件,并通过 props 传递数据和监听事件:


<template>
  <div>
    <p>Parent Component</p>
    <ChildComponent @message-from-child="receiveMessage" />
    <p>{{ receivedMessage }}</p>
  </div>
</template>

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

export default {
  components: {
    ChildComponent: Vue.extend(ChildComponent)
  },
  data() {
    return {
      receivedMessage: ''
    };
  },
  methods: {
    receiveMessage(message) {
      this.receivedMessage = message;
    }
  }
};
</script>
在这个例子中,父组件通过 ChildComponent 组件的 @message-from-child 事件监听来接收子组件发送的消息。子组件通过 this.$emit() 发送消息给父组件。

通过这种方式,你可以在 Vue 中实现父子组件之间的数据传递和事件通信。这种方式是 Vue 组件之间通讯的基础,非常常用和有效。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值