一、props
概述:props是使用频率最高的一种通信方式,常用与 :父 ↔ 子。
若 父传子:属性值是非函数。若 子传父:属性值是函数。1.1、父组件登录后复制
<template>
<div class="app">
<h2>父组件</h2>
<p v-if="drug">
子组件中的药剂:{{ drug }}
</p>
<p>
余额:{{ amount }}
</p>
<hr />
<Subcomponent :money="amount" :sendDrug="getDrug" />
</div>
</template>
<script setup lang="ts" name="App">
import Subcomponent from ./components/Subcomponent.vue;
import { ref } from vue;
// 数据
let amount = ref(666)
let drug = ref()
// 方法
function getDrug(d: string) {
drug.value = d
}
</script>
<style lang="scss" scoped>
.app {
margin: 150px;
padding: 30px;
background-color: bisque;
}
</style>1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.
登录后复制
<template>
<div class="subcomponent">
<h2>子组件</h2>
<p>
父组件中的余额:{{ money }}
</p>
<p>
药剂:{{ drug }}
</p>
<button @click="sendDrug(drug)">传你一副药剂</button>
</div>
</template>
<script setup lang="ts" name="Subcomponent">
import { ref } from vue;
// 接收父组件传来的参数
defineProps([money, sendDrug])
// 或者下面这样接收。下面这种接收方式就可以在当前setup中直接调用sendDrug(xxx)
// const props = defineProps<{money?: number, sendDrug: Function}>()
// let { money, sendDrug } = props
// sendDrug(xxx)
let drug = ref(春嘿嘿)
</script>
<style lang="scss" scoped>
.subcomponent {
margin: 50px;
padding: 50px;
background-color: coral;
}
button {
margin-right: 20px;
background-color: beige;
}
</style>1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.
二、Pinia
2.1、在src/store目录下新建一个xxx.ts文件。(这就相当于共享数据)登录后复制
import axios from "axios";
import { defineStore } from "pinia";
import { reactive } from "vue";
const useLoveTalkStore = defineStore(love-talk-id, () => {
// 相当于[state-状态] 先从浏览器的 localStorage 中取值
const loveTalks: string[] = reactive(JSON.parse(localStorage.getItem(loveTalks) as string) || [])
// 相当于[actions-动作]
function addLoveTalk() {
// loveTalks 数组头部插入
loveTalks.unshift(content)
// 存储到浏览器的 localStorage 中,实现页面刷新数据不丢失
localStorage.setItem(loveTalks, JSON.stringify(loveTalks))
}
// 向外暴露
return { loveTalks }
})
// 暴露useLoveTalkStore
export default useLoveTalkStore1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.
登录后复制
// 引入loveTalk.ts
import useLoveTalkStore from ./store/loveTalk;
import { storeToRefs } from pinia;
import { toRefs } from vue;
let loveTalkStore = useLoveTalkStore()
// 可以直接从 useLoveTalkStore() 结果中解构方法
const { addLoveTalk } = loveTalkStore
const { loveTalks } = storeToRefs(loveTalkStore)1.2.3.4.5.6.7.8.9.
三、mitt(其实就是先在一个组件中绑定事件,然后其他组件只需要触发这个事件就能完成组件之间的通信。只不过你想做什么样的操作,就需要先绑定什么样的事件)
3.1、安装mitt登录后复制
npm i mitt1.
登录后复制
// 引入mitt
import mitt from "mitt";
// 创建mitt
const emitter = mitt()
// 暴露mitt
export default emitter1.2.3.4.5.6.7.8.
登录后复制
<template>
<div class="subcomponent">
<h2>子组件</h2>
<p>
电脑:{{ computer }}
</p>
</div>
</template>
<script setup lang="ts" name="Subcomponent">
import emitter from @/utils/emitter;
import { onUnmounted, ref } from vue;
// 数据
let computer = ref<string>(惠普-暗影精灵)
// 组件创建时,绑定事件[modify-computer]
emitter.on(modify-computer, (value: any) => {
computer.value = value
})
// 组件卸载时,解绑事件!!!!!!
onUnmounted(() => {
emitter.off(modify-computer)
})
</script>
<style lang="scss" scoped>
.subcomponent {
margin: 50px;
padding: 50px;
background-color: coral;
}
button {
margin-right: 20px;
background-color: beige;
}
</style>1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.
登录后复制
<template>
<div class="app">
<h2>父组件</h2>
<button @click="changeSubComputer">改变子组件中的电脑</button>
<hr />
<Subcomponent />
</div>
</template>
<script setup lang="ts" name="App">
import Subcomponent from ./components/Subcomponent.vue;
import emitter from ./utils/emitter;
// 方法。触发子组件中的事件
function changeSubComputer() {
emitter.emit(modify-computer, 联想)
}
</script>
<style lang="scss" scoped>
.app {
margin: 150px;
padding: 30px;
background-color: bisque;
}
</style>1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.
四、slot【作用域插槽】
数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。
4.1、定义一个共用组件Subcomponent.vue。(数据和插槽都在当前组件中)登录后复制
<template>
<div class="subcomponent">
<!-- <h2>子组件</h2> -->
<!-- 给插槽绑定数据,谁使用了插槽就能拿到这些数据 -->
<slot name="s1" :persons="persons" :ss="123">未知异常</slot>
</div>
</template>
<script setup lang="ts" name="Subcomponent">
import { reactive } from vue;
// 数据和插槽在同一个组件中。(可重复使用当前组件来实现同一数据不同样式)
let persons = reactive([
{ id: 001, name: 张三 },
{ id: 002, name: 李四 },
{ id: 003, name: 王五 },
{ id: 004, name: 赵六 }
])
</script>
<style lang="scss" scoped>
.subcomponent {
background-color: skyblue;
border-radius: 10px;
box-shadow: 0 0 10px;
padding: 10px;
width: 200px;
height: 300px;
}
button {
margin-right: 20px;
background-color: beige;
}
</style>1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.
使用插槽时,可以在template中使用#s1="params"来获取插槽中绑定的数据,并通过params.persons、params.ss的方式取出绑定的多个值。
登录后复制
<template>
<div class="app">
<!-- <h2>父组件</h2> -->
<!-- <hr /> -->
<!-- #s1 相当于 v-slot:s1 -->
<div class="content">
<Subcomponent>
<!-- #s1="params":取出插槽中绑定的所有数据。也可以不叫params,取名随意。 -->
<template #s1="params">
<h2>有序列表</h2>
<ul>
<li v-for="(p, index) in params.persons" :key="p.id">
{{ p.name }}
</li>
</ul>
<p>
<span>展示测试的值:{{ params.ss }}</span>
</p>
</template>
</Subcomponent>
<Subcomponent>
<template v-slot:s1="params">
<h2>有序列表</h2>
<ol>
<li v-for="(p, index) in params.persons" :key="p.id">
{{ p.name }}
</li>
</ol>
</template>
</Subcomponent>
<Subcomponent>
<template #s1="params">
<h2>人物列表</h2>
<p v-for="(p, index) in params.persons" :key="p.id">
{{ p.name }}
</p>
<!-- <h2>短视频</h2> -->
<!-- <video :src="videoUrl" controls /> -->
</template>
</Subcomponent>
</div>
</div>
</template>
<script setup lang="ts" name="App">
import Subcomponent from "./components/Subcomponent.vue";
import { ref, reactive } from "vue";
// 当前页面不需要定义数据,数据都是从插槽中获取。
// let persons = reactive([
// { id: 001, name: 张三 },
// { id: 002, name: 李四 },
// { id: 003, name: 王五 },
// { id: 004, name: 赵六 }
// ])
// let imgUrl = ref(https://img1.baidu.com/it/u=2723523495,2549185469&fm=253&fmt=auto&app=120&f=JPEG?w=1280&h=800"})
// let videoUrl = ref(https://media.w3.org/2010/05/sintel/trailer.mp4)
</script>
<style lang="scss" scoped>
.app {
background-color: pink;
border-radius: 10px;
width: 800px;
margin: auto;
margin-top: 100px;
padding: 20px;
}
.content {
display: flex;
justify-content: space-evenly;
}
img,video {
width: 100%;
}
h2 {
background-color: orange;
text-align: center;
font-size: 20px;
font-weight: 800;
}
</style>1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64.65.66.67.68.69.70.71.72.73.74.75.76.77.78.79.80.81.82.83.84.85.86.87.88.89.90.
五、自定义事件
5.1、在父组件App.vue中对引入的子组件Subcomponent.vue添加自定义事件<Subcomponent @modify-car="car = $event" />。登录后复制
<template>
<div class="app">
<h2>父组件</h2>
<p>
车:{{ car }}
</p>
<hr />
<!-- 添加自定义事件 -->
<Subcomponent @modify-car="car = $event" />
</div>
</template>
<script setup lang="ts" name="App">
import { ref } from vue;
import Subcomponent from ./components/Subcomponent.vue;
let car = ref(法拉利)
</script>
<style lang="scss" scoped>
.app {
margin: 150px;
padding: 30px;
background-color: bisque;
}
</style>1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.
登录后复制
<template>
<div class="subcomponent">
<h2>子组件</h2>
<button @click="modifyCar">修改父组件中的车</button>
</div>
</template>
<script setup lang="ts" name="Subcomponent">
// 声明组件可以触发的自定义事件
const emit = defineEmits([modify-car])
// 方法
function modifyCar() {
// 触发自定义事件
emit(modify-car, 五菱宏光)
}
</script>
<style lang="scss" scoped>
.subcomponent {
margin: 50px;
padding: 50px;
background-color: coral;
}
button {
margin-right: 20px;
background-color: beige;
}
</style>1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.
六、$attrs
$attrs用于实现当前组件的父组件,向当前组件的子组件通信(祖→孙)。
$attrs是一个对象,包含所有父组件传入的标签属性。
注意:$attrs会自动排除props中声明的属性(可以认为声明过的 props 被子组件自己“消费”了)
6.1、父组件Parentcomponent.vue。(在引入的子组件标签中传递属性值)登录后复制
<template>
<div class="parentcomponent">
<h2>父组件: [a = {{ a }}] [b = {{ b }}] [c = {{ c }}]
</h2>
<hr />
<!-- 传入a, b, c, modifyName 等标签属性 -->
<Subcomponent :a="a" :b="b" :c="c" :modifyName="modifyName" />
</div>
</template>
<script setup lang="ts" name="Parentcomponent">
import { reactive, ref } from vue;
import Subcomponent from ./Subcomponent.vue;
// 数据
let a = ref(1)
let b = ref(qq)
let c = reactive({ id: 001, name: 张三 })
// 方法
function modifyName(value: string): void {
Object.assign(c, { name: value })
}
</script>
<style lang="scss" scoped>
.parentcomponent {
margin: auto;
margin-top: 50px;
padding: 30px;
background-color: antiquewhite;
width: 900px;
}
</style>1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.
登录后复制
<template>
<div class="subcomponent">
<h2>子组件</h2>
<hr />
<!-- 需要使用 v-bind="$attrs" 。 否则孙组件中不会接收到父组件中传来的标签属性 -->
<Grandsoncomponent v-bind="$attrs" />
</div>
</template>
<script setup lang="ts" name="Subcomponent">
import Grandsoncomponent from ./Grandsoncomponent.vue;
// $attrs会自动排除以下props中声明的属性
// 如下定义,则孙组件无法获取父组件传来的属性a的值
defineProps([a])
</script>
<style lang="scss" scoped>
.subcomponent {
margin: auto;
margin-top: 30px;
padding: 30px;
background-color: bisque;
}
</style>1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.
登录后复制
<template>
<div class="grandsoncomponent">
<h2>孙组件</h2>
<p>
接收到父组件中的值: [a = {{ a }}] [b = {{ b }}] [c = {{ c
}}]
</p>
<button @click="changeNameForParentcomponent">更新父组件中的名字</button>
</div>
</template>
<script setup lang="ts" name="Grandsoncomponent">
import { toRefs } from vue;
// 声明并接收父组件中传来props属性
const props = defineProps<{ a?: number, b?: string, c?: object, modifyName?: Function }>()
console.log(props === , props);
const { a, b, c } = toRefs(props)
function changeNameForParentcomponent() {
// ?. modifyName 是函数就调用它;否则不执行任何操作
props.modifyName?.(李四~~~)
}
</script>
<style lang="scss" scoped>
.grandsoncomponent {
margin: auto;
margin-top: 30px;
padding: 30px;
background-color: beige;
}
</style>1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.
七、$refs、$parent
$refs:值为对象,包含所有被ref属性标识的DOM元素或组件实例。常用于 :父→子。
$parent:值为对象,当前组件的父组件实例对象。常用于:子→父。
7.1、父组件Father.vue(通过$refs拿到当前组件的所有子组件对象,并且可对子组件暴露的数据进行修改)登录后复制
<template>
<div class="father">
<h3>父组件</h3>
<h4>房产:{{ house }}</h4>
<button @click="changeToy">修改Child1的玩具</button>
<button @click="changeComputer">修改Child2的电脑</button>
<!-- $refs 能拿到当前组件的所有子组件。若要拿到子组件中的数据或方法则需要子组件使用defineExpose暴露 -->
<button @click="getAllChild($refs)">让所有孩子的书变多</button>
<!-- ref 需要绑定一个变量,例如 c1 是需要自己定义的 -->
<Child1 ref="c1" />
<Child2 ref="c2" />
</div>
</template>
<script setup lang="ts" name="Father">
import Child1 from ./Child1.vue
import Child2 from ./Child2.vue
import { ref } from "vue";
// 数据
let house = ref(4)
let c1 = ref()
let c2 = ref()
// 方法
function changeToy() {
c1.value.toy = 小猪佩奇
}
function changeComputer() {
c2.value.computer = 华为
}
// $refs 能拿到当前组件的所有子组件。若要拿到子组件中的数据或方法则需要子组件使用defineExpose暴露
function getAllChild(refs: { [key: string]: any }) {
console.log($refs === , refs)
for (let key in refs) {
refs[key].book += 3
}
}
// 向外部提供数据
defineExpose({ house })
</script>
<style lang="scss" scoped>
.father {
background-color: pink;
padding: 20px;
border-radius: 10px;
}
.father button {
margin-bottom: 10px;
margin-left: 10px;
}
button {
background-color: bisque;
}
</style>1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.
登录后复制
<template>
<div class="child1">
<h3>子组件1</h3>
<h4>玩具:{{ toy }}</h4>
<h4>书籍:{{ book }} 本</h4>
<!-- $parent 可以拿到当前组件的父组件。若要拿到父组件中的数据或方法则需要父组件使用defineExpose暴露 -->
<button @click="minusHouse($parent)">干掉父亲的一套房产</button>
</div>
</template>
<script setup lang="ts" name="Child1">
import { ref } from "vue";
// 数据
let toy = ref(奥特曼)
let book = ref(3)
// 方法
function minusHouse(parent: any) {
console.log($parent === , parent)
parent.house -= 1
}
// 把数据交给外部
defineExpose({ toy, book })
</script>
<style lang="scss" scoped>
.child1 {
margin-top: 20px;
background-color: skyblue;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px black;
}
</style>1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.
登录后复制
<template>
<div class="child2">
<h3>子组件2</h3>
<h4>电脑:{{ computer }}</h4>
<h4>书籍:{{ book }} 本</h4>
</div>
</template>
<script setup lang="ts" name="Child2">
import { ref } from "vue";
// 数据
let computer = ref(联想)
let book = ref(6)
// 把数据交给外部
defineExpose({ computer, book })
</script>
<style lang="scss" scoped>
.child2 {
margin-top: 20px;
background-color: orange;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px black;
}
</style>1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.
八、provide、inject
8.1、父组件Parentcomponent.vue。(通过provide向后代组件提供数据)登录后复制
<template>
<div class="parentcomponent">
<h2>父组件</h2>
<h4>资产:{{ money }}</h4>
<h4>汽车:{{ car }}</h4>
<button @click="money += 1">资产+1</button>
<button @click="car.price += 1">汽车价格+1</button>
<hr />
<Subcomponent />
</div>
</template>
<script setup lang="ts" name="Parentcomponent">
import Subcomponent from "./Subcomponent.vue";
import { ref, reactive, provide } from "vue";
// 数据
let money = ref(100)
let car = reactive({
brand: 奔驰,
price: 100
})
// 用于更新money的方法
function updateMoney(value: number) {
money.value += value
}
// 提供数据
provide(moneyContext, { money, updateMoney })
provide(car, car)
</script>
<style lang="scss" scoped>
.parentcomponent {
margin: auto;
margin-top: 50px;
padding: 30px;
background-color: antiquewhite;
width: 900px;
}
button {
margin-right: 10px;
background-color: bisque;
}
</style>
1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.
登录后复制
<template>
<div class="subcomponent">
<h2>子组件</h2>
<hr />
<Grandsoncomponent />
</div>
</template>
<script setup lang="ts" name="Subcomponent">
import Grandsoncomponent from ./Grandsoncomponent.vue;
</script>
<style lang="scss" scoped>
.subcomponent {
margin: auto;
margin-top: 30px;
padding: 30px;
background-color: bisque;
}
</style>
1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.
登录后复制
<template>
<div class="grandsoncomponent">
<h3>孙组件</h3>
<h4>资产:{{ money }}</h4>
<h4>汽车:{{ car }}</h4>
<!-- 父组件中的方法也可通过provide提供 -->
<button @click="updateMoney(6)">点我资产增加</button>
</div>
</template>
<script setup lang="ts" name="Grandsoncomponent">
import { inject } from vue;
// 注入数据
let { money, updateMoney } = inject(moneyContext, { money: 0, updateMoney: (x: number) => {} })
let car = inject(car)
</script>
<style lang="scss" scoped>
.grandsoncomponent {
margin: auto;
margin-top: 30px;
padding: 30px;
background-color: beige;
}
</style>
1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.
九、v-model
9.1、父组件App.vue。(v-model的本质::moldeValue + update:modelValue事件)登录后复制
<template>
<div class="app">
<!-- 下面两个标签是等价的 -->
<MyInput v-model:qqq="username" />
<!-- v-model的本质是下面这行代码 -->
<!-- <MyInput :qqq="username" @update:qqq="username = $event" /> -->
</div>
</template>
<script setup lang="ts" name="App">
import MyInput from ./components/MyInput.vue;
import { ref } from vue;
// 数据
let username = ref(zhangsan)
</script>
<style lang="scss" scoped>
.app {
width: 700px;
margin: auto;
}
</style>
1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.
登录后复制
<template>
<div class="myInput">
输入框:
<!-- 将接收的qqq值赋给input元素的value属性,目的是:为了呈现数据 -->
<!-- 给input元素绑定原生input事件,触发input事件时,进而触发update:qqq事件 -->
<!-- v-model的本质是下面这行代码 -->
<input
type="text"
:value="qqq"
@input="allEmits(update:qqq, (<HTMLInputElement>$event.target).value)"
/>
</div>
</template>
<script setup lang="ts" name="MyInput">
// 声明组件接收的 props
defineProps([qqq])
// 声明组件可以触发的自定义事件
const allEmits = defineEmits([update:qqq])
</script>
<style lang="scss" scoped>
input {
border: 2px solid rgb(0, 0, 0);
background-image: linear-gradient(45deg, rgb(248, 194, 194), rgba(189, 242, 248, 0.552), rgb(249, 208, 218));
height: 30px;
font-size: 20px;
color: rgb(255, 5, 5);
}
</style>
1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.
未经允许不得转载:AiShang - 爱尚IT分享博客 » Vue3 组件通信