Vue 3 组件通信与状态管理:从基础到Pinia的全面解析

引言

Vue 3引入了Composition API,这为组件通信提供了更多的灵活性和强大的功能。例如,使用provideinject可以轻松实现跨层级组件的数据传递,而setup函数则为组件提供了更早的生命周期钩子,使得在组件初始化阶段就能进行通信设置。

父子组件通信的基本方式(props和$emit)

在Vue 3中,父子组件通信主要通过props和$emit实现。

  • Props:父组件通过props向子组件传递数据。在<script setup>中,使用defineProps来定义接收的props。
<!-- 父组件 -->
<template>
  <ChildComponent :parentMessage="message" />
</template>

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

const message = ref('Hello from parent!');
</script>

<!-- 子组件 -->
<script setup>
import { defineProps } from 'vue';

const props = defineProps({
  parentMessage: String
});

console.log(props.parentMessage); // 输出: Hello from parent!
</script>
  • 𝑒𝑚𝑖𝑡:子组件通过emit:子组件通过emit方法触发一个事件,并将数据作为参数传递给父组件。在<script setup>中,使用defineEmits`来声明可以触发的事件。
<!-- 子组件 -->
<script setup>
import { defineEmits } from 'vue';

const emit = defineEmits(['child-event']);

function triggerEvent() {
  emit('child-event', 'Data from child');
}
</script>
自定义事件的使用和原理

自定义事件在Vue中用于父子组件间的通信。父组件通过监听子组件发出的自定义事件来接收数据或执行某些操作。

  • 使用自定义事件:在<script setup>中,使用defineEmits来声明可以触发的事件,并在模板中使用v-on(或简写为@)来监听这些事件。
<!-- 父组件 -->
<template>
  <ChildComponent @custom-event="handleCustomEvent" />
</template>

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

function handleCustomEvent(data) {
  console.log(data); // 输出: Custom event data
}
</script>

<!-- 子组件 -->
<script setup>
import { defineEmits } from 'vue';

const emit = defineEmits(['custom-event']);

function triggerCustomEvent() {
  emit('custom-event', 'Custom event data');
}
</script>

简单的说就是,父组件用@×××占位表示接受子组件传过来的数据,:×××来向子组件传数据 ,子组件的话就是上面所示的defineProps方法和defineEmit来接受和向父组件传数据

在Vue 3中,v-model指令用于实现父子组件间的双向数据绑定。它是一个语法糖,背后实际上是结合了value prop和input事件的使用。v-model在组件上使用时,可以简化父组件和子组件之间的数据传递和事件处理。

vue3中的v-model

基本用法

假设我们有一个父组件和一个子组件,子组件需要实现一个输入框,父组件需要能够获取子组件输入框的值,并且当子组件的输入框值改变时,父组件的值也要相应更新。

子组件

在子组件中,我们定义一个props来接收父组件传递的值,并定义一个emit来向父组件发送值的变化。

<!-- ChildComponent.vue -->
<template>
  <input type="text" :value="modelValue" @input="updateValue" />
</template>

<script setup>
import { defineProps, defineEmits } from 'vue';

const props = defineProps({
  modelValue: String
});

const emit = defineEmits(['update:modelValue']);

const updateValue = (event) => {
  emit('update:modelValue', event.target.value);
};
</script>
父组件

在父组件中,我们使用v-model指令来绑定子组件的modelValue prop和父组件的某个数据属性。

<!-- ParentComponent.vue -->
<template>
  <ChildComponent v-model="parentData" />
</template>

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

const parentData = ref('');
</script>

高级用法

Vue 3还允许我们自定义v-model的行为,例如,我们可以改变v-model绑定的prop名称,或者改变触发更新的事件名称。

自定义prop和事件

在子组件中,我们可以使用defineEmits来声明自定义的事件名称,并在update:modelValue事件中使用它。

<!-- ChildComponent.vue -->
<script setup>
import { defineProps, defineEmits } from 'vue';

const props = defineProps({
  customModelValue: String
});

const emit = defineEmits(['update:customModelValue']);

const updateValue = (event) => {
  emit('update:customModelValue', event.target.value);
};
</script>

在父组件中,我们使用v-model:customModelValue来绑定自定义的prop。

<!-- ParentComponent.vue -->
<template>
  <ChildComponent v-model:customModelValue="parentData" />
</template>

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

const parentData = ref('');
</script>

 

使用provide和inject实现跨层级通信

provideinject是Vue 3提供的两个新的API,用于实现跨层级组件的通信。provide函数允许你定义一个值,这个值可以被所有子组件注入。inject函数允许你注入一个由祖先组件提供的值。

<!-- 祖先组件 -->
<script setup>
import { provide } from 'vue';

provide('globalState', reactive({ count: 0 }));
</script>

<!-- 子组件 -->
<script setup>
import { inject } from 'vue';

const globalState = inject('globalState');
</script>

父组件通过provide传递的数据,子组件和子组件的子组件,以及子组件的子组件的子组件······(可多重套娃)都能通过inject接收

使用Pinia

Pinia 如何共享数据

Pinia 通过创建 store 来共享数据。store 是一个包含状态(state)、getter 和 actions 的容器。在 Pinia 中,store 是响应式的,这意味着当状态改变时,所有使用该状态的组件都会自动更新。

创建 Store

创建一个简单的计数器 store:

// stores/counterStore.js
import { defineStore } from 'pinia';

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
  }),
  actions: {
    increment() {
      this.count++;
    },
    decrement() {
      this.count--;
    },
  },
});
使用 Store

在组件中,你可以通过 useStore 函数来访问 store:

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

<script setup>
import { useCounterStore } from '../stores/counterStore';

const counterStore = useCounterStore();

const count = computed(() => counterStore.count);
const increment = () => counterStore.increment();
const decrement = () => counterStore.decrement();
</script>
Pinia分包 

开发的产品大了,一个store肯定满足不了,我们可以这么搞:

步骤 1: 创建模块

在 src/stores/modules 目录下创建模块文件,例如 counterModule.js

// src/stores/modules/counterModule.js
import { defineStore } from 'pinia';

export const useCounterModule = defineStore('counterModule', {
  state: () => ({
    count: 0,
    }),
  actions: {
    increment() {
      this.count++;
      },
    decrement() {
      this.count--;
      },
    },
});

步骤 2: 创建主 store

在 src/stores 目录下创建主 store 文件,例如 index.js

// src/stores/index.js
import { createPinia } from 'pinia';
import * as modules from './modules';

export const useMainStore = createPinia();

// 注册所有模块
Object.keys(modules).forEach((key) => {
  useMainStore.use(modules[key]);
});

步骤 3: 在主文件(如main.js)中使用主 store

在您的主文件(通常是 main.js 或 main.ts)中,使用 useMainStore 来引入主 store:

// main.js
import { createApp } from 'vue';
import App from './App.vue';
import { useMainStore } from './stores';

const app = createApp(App);

// 使用主 store
app.use(useMainStore);

app.mount('#app');

步骤 4: 在组件中使用模块

在组件中,您可以直接通过模块的名称来访问模块中的状态和 actions:

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

<script setup>
import { useMainStore } from '../stores';

const mainStore = useMainStore();

const count = computed(() => mainStore.counterModule.count);
const increment = () => mainStore.counterModule.increment();
const decrement = () => mainStore.counterModule.decrement();
</script>

把所有pinia都导入到主store,这样无论调用哪个pinia都只用访问主store即可

 总结

组件通信方式

  1. Props & $emit

    • Props:从父组件向子组件传递数据。
    • $emit:子组件向父组件传递数据或触发事件。
  2. 自定义事件

    • 子组件使用defineEmits声明可触发的事件。
    • 父组件使用v-on@监听子组件的事件。
  3. v-model

    • 实现父子组件间的双向数据绑定。
    • 子组件通过modelValue prop和update:modelValue事件与父组件交互。
  4. provide/inject

    • 跨层级组件间通信,无需逐层传递props。
    • provide在祖先组件中提供数据。
    • inject在后代组件中注入数据。
  5. Pinia

    • 用于状态管理,创建和使用store来共享和管理应用的状态。
    • 可以创建多个store模块并组合使用。

如果这篇文章有帮助到你,给个点赞好吗

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值