组件通信方式
2023年9月1日大约 6 分钟组件通信vue
组件通信方式
方式一: provide 和 inject
用于多层传值,provide 在父组件中使用,inject 在后代中使用。无论组件层次结构有多深,父组件都可以作为其所有子组件的依赖
提供者。
<template>
<!-- 我是父组件 -->
<Child></Child>
</template>
<script setup>
import { provide, ref, readonly } from "vue";
import Child from "./components/Child.vue";
// 数据
const name = ref("小明同学");
const msg = ref("真好喝!");
// provide 方法
// 使用 readonly 可以让子组件无法直接修改,需要调用 provide 往下传的方法来修改
provide("name", readonly(name));
provide("msg", msg);
provide("changeName", (value) => {
name.value = value;
});
</script>
```vue
<template>
<div>
<!-- 我是孩子组件 -->
<div>msg: {{ msg }}</div>
<div>name: {{ name }}</div>
<button @click="handleClick">修改</button>
</div>
</template>
<script setup>
import { inject } from "vue";
// 从 inject 中取值
const msg = inject("msg", "默认值");
const name = inject("name", "默认值");
const changeName = inject("changeName");
// 自身的方法
const handleClick = () => {
changeName("小爱同学");
msg.value = "来了!";
};
</script>
```
## 方式二: Props
> 只能实现父传子,优点是简单,用的概况大。
```vue
<template>
<!-- 我是父组件 -->
<Son :list="DataList"></Son>
</template>
<script setup>
import Son from "./components/Son.vue";
import { ref } from "vue";
let DataList = ref([]);
</script>
```
```vue
<template>
<!-- 我是子组件 -->
<div v-for="item in DataList"></div>
</template>
<script setup>
const props = defineProps({
DataList: {
type: Array,
default: [],
required: true,
},
});
// 在 JS 中使用,是使用 props.DataList
// 在 template 中使用,直接使用即可
</script>
```
## 方式三: emits
> 子组件通知父组件触发一个事件,并且可以传值给父组件。
```vue
<template>
<!-- 我是父组件 -->
<div>{{ msg }}</div>
<!-- 自定义事件 changeMsg -->
<Child @changeMsg="changeMessage"></Child>
</template>
<script setup>
import Child from "./components/Child.vue";
import { ref } from "vue";
let message = ref("小爱同学");
// 此时的 data 是由子组件传递过来的值
const changeMessage = (data) => {
message.value = data;
};
</script>
```
```vue
<template>
<div>子组件:<button @click="handleClick">子组件的按钮</button></div>
</template>
<script setup>
// 可以是有多个自定义的事件,此处填上自定义事件名
const emit = defineEmits(["changeMsg", "多个自定义的事件"]);
// 选择调用的时机
const handleClick = () => {
// 参数1:事件名
// 参数2:传给父组件的值
emit("changeMsg", "参数2:传给父组件的值");
};
</script>
```
## 方式四: ref 和 expose
> 子组件通过 expose 来暴露自身的方法和数据。
> 父组件通过给子组件打上 ref,来获取子组件并调用其方法或访问数据。
```vue
<template>
<div>父组件拿到子组件数据: {{ msg }}</div>
<button @click="callChildFn">父组件调用子组件方法:</button>
<Child ref="Son"></Child>
</template>
<script setup>
import { ref, onMounted } from "vue";
import Child from "./components/Child.vue";
// 通过模板 ref 绑定子组件,名字就是绑定那个 ref 绑定的值
const Son = ref(null);
const msg = ref("");
// 加载完成后,将子组件的值赋给父组件,需要保证子组件有那个方法和属性
onMounted(() => {
msg.value = Son.value.message;
});
const callChildFn = () => {
// 子组件的方法,直接调用
Son.value.changeMessage("小爱同学");
// 此时子组件的值已经被改变了,重新赋给父组件
msg.value = Son.value.message;
};
</script>
```
```vue
<template>
<div>子组件:{{ message }}</div>
</template>
<script setup>
import { ref } from "vue";
const message = ref("小明同学");
const changeMessage = (data) => {
message.value = data;
};
// 此处父组件仍然不能够拿到子组件的方法和属性,需要对外暴露
defineExpose({
message,
changeMessage,
});
</script>
```
## 方式五: 非 Prop 的 Attribute(属性,特征)
> 没使用 prop 或 emits 定义的 attribute,可以通过 $attrs 来访问,但是不如 props,可以控制类型,所以不推荐使用。
> 分为单根组件和多根组件两种情况。
```vue
<template>
<!-- 父组件 -->
<Child msg="小明同学" message="小爱同学"></Child>
</template>
```
```vue
<template>
<!-- 子组件:打开控制台看看 -->
</template>
```
`传递的信息会被直接挂载到 "<div>" 上。`
`若子组件的根组件不是一个时,需要通过"$attrs"的方式去绑定。`
```
vue
<template>
<div :message="$attrs.msg">只绑定某个属性</div>
<div v-bind="$attrs">全绑定</div>
</template>
```
## 方式六: v-model
> v-model 是 Vue 的一个语法糖,双向数据流。应该常用于 (element) 的组件值绑定。
```vue
<template>
父组件
<Child v-model="message" />
</template>
<script setup>
import { ref } from "vue";
import Child from "./components/Child.vue";
const message = ref("雷猴");
</script>
```
```vue
<template>
子组件
<div @click="handleClick">{{ modelValue }}</div>
</template>
<script setup>
import { ref } from "vue";
// 接收父子件的 v-model 值
const props = defineProps([
"modelValue", // 接收父组件使用 v-model 传进来的值,必须用 modelValue 这个名字来接收
]);
const emit = defineEmits(["update:modelValue"]); // 必须用 update:modelValue 这个名字来通知父组件修改值
function handleClick() {
// 参数1:通知父组件修改值的方法名
// 参数2:要修改的值
emit("update:modelValue", "我是子组件传递给父组件的值");
}
</script>
```
> 绑定多个值(v-model)
```vue
<template>
父组件
<Child v-model:msg1="message1" v-model:msg2="message2" />
</template>
<script setup>
import { ref } from "vue";
import Child from "./components/Child.vue";
const message1 = ref("雷猴");
const message2 = ref("雷猴");
</script>
```
```vue
<template>
子组件
<div><button @click="changeMsg1">修改msg1</button> {{ msg1 }}</div>
<div><button @click="changeMsg2">修改msg2</button> {{ msg2 }}</div>
</template>
<script setup>
import { ref } from "vue";
const props = defineProps({
msg1: string,
msg2: string,
});
const emit = defineEmits(["update:msg1", "update:msg2"]);
const changeMsg1 = () => {
emit("update:msg1", "传数据改父组件message1");
};
const changeMsg2 = () => {
emit("update:msg2", "传数据改父组件message2");
};
</script>
```
## 方式七: Bus 总线
> 弄一个独立的工具出来专门控制数据。
> 新建一个 Bus.js,用来控制数据和注册事件的,构建一个 BUS 类。
- eventList 是必须项,用来存放事件列表的。
- constructor 里除了 eventList 外,其他都是自定义数据,公共数据就是存在这里的。
- $on 方法用来注册事件。
- $emit 方法可以调用 $on 里的事件。
- $off 方法可以注销 eventList 里的事件。
```js
import { ref } from "vue";
class Bus {
constructor() {
// 初始化事件函数
this.eventList = {};
// 自定义值
this.msg = ref("欣小萌");
}
// 订阅
$on(name, fn) {
this.eventList[name] = this.eventList[name] || [];
this.eventList[name].push(fn);
}
// 发布
$emit(name, data) {
if (this.eventList[name]) {
this.eventList[name].forEach((fn) => {
fn(data);
});
}
}
// 取消订阅
$off(name) {
if (this.eventList[name]) {
delete this.eventList[name];
}
}
}
export default new Bus();
```
```vue
<template>
<div>需要使用 BUS 的组件01</div>
<div>{{ msg }}</div>
<Child></Child>
</template>
<script setup>
import Bus from "./Bus.js";
import Child from "./components/Child.vue";
// 直接去拿 Bus 中定义的数据
const msg = ref(Bus.msg);
// 定义自己的数据
const message = ref("hello");
// 用监听的写法 给 BUS 类 加方法 改变需要使用 BUS 的组件01的值
Bus.$on("changeMsg", (data) => {
message.value = data;
});
</script>
```
```vue
<template>
<div>
<div>需要使用 BUS 的组件02</div>
<button @click="handleBusEmit">触发 Bus.$emit</button>
<button @click="changeBusMsg">修改总线里的 msg</button>
</div>
</template>
<script setup>
import Bus from "../Bus.js";
// 触发事件总线中 changeMsg 的方法。后面是传入的值
const handleBusEmit = () => {
Bus.$emit("changeMsg", "小明同学");
};
// 直接在其他组件中修改 BUS 中的值 (不推荐直接使用)
const changeBusMsg = () => {
Bus.msg.value = "在子组件里修改了总线的值";
};
</script>
```
## 方式八: vuex
> Vue 官方推荐的全局状态管理方案
希望这有助于您将文档内容转换为漂亮的 Markdown 格式!如果您需要更多帮助或有其他问题,请随时提问。