props的概念:
当你使用Vue 3的组合式API时,props就是一种让你可以从父组件向子组件传递数据的方式。你可以想象成你在给子组件写一封信,把需要传递的信息放在信封里。
在Vue 3中,你可以在子组件的代码中定义props,就像在信封上写下你需要传递的信息的名字。你可以指定每个props的类型,比如是字符串、数字等等,这样Vue就会帮你确保接收到的数据是正确的类型。
你还可以指定props是否是必需的,就像是你在信封上写着"必须打开",这样别人在使用你的组件时就必须传递这个props,否则会出现警告。
如果你给props设置了默认值,就像是在信封里放了一张字条,如果别人没有传递这个props,那么组件就会使用默认值。
当父组件给子组件传递了props,子组件就可以在自己的代码中使用这些props了,就像是打开了你传递的信封,取出里面的信息。你可以根据需要对这些props进行处理和计算,就像是读取信中的内容并进行一些操作。
假设在App组件的框架下,里面有众多的组件,其中A组件的数据和B组件如何进行交互呢?
答案:组件通信
1.组件通信方式1-props
若父传子:属性值是非函数。
如图:左边是父组件,右边是子组件
若 **子传父**:属性值是**函数**。
子传父:
- 接受子子组件传来的东西需要提前定义一个方法getToy
- 把getToy传给子组件
- 子那边要照样接收
- 直接使用父传来的,记得要加参数
Child.vue子组件代码:
<template>
<div class="child">
<h3>子组件</h3>
<h4>玩具:{{ toy }}</h4>
<h4>父给的车:{{ car }}</h4>
<button @click="sendToy(toy)">把玩具给父亲</button>
</div>
</template>
<script setup lang="ts" name="Child">
import {ref} from 'vue'
// 数据
let toy = ref('奥特曼')
// 声明接收props
defineProps(['car','sendToy'])
</script>
<style scoped>
.child{
background-color: skyblue;
padding: 10px;
box-shadow: 0 0 10px black;
border-radius: 10px;
}
</style>
Father.vue父组件代码
<template>
<div class="father">
<h3>父组件</h3>
<h4>汽车:{{ car }}</h4>
<h4 v-show="toy">子给的玩具:{{ toy }}</h4>
<Child :car="car" :sendToy="getToy"/>
</div>
</template>
<script setup lang="ts" name="Father">
import Child from './Child.vue'
import {ref} from 'vue'
// 数据
let car = ref('奔驰')
let toy = ref('')
// 方法
function getToy(value:string){
toy.value = value
}
</script>
<style scoped>
.father{
background-color:rgb(165, 164, 164);
padding: 20px;
border-radius: 10px;
}
</style>
补充:
v-show指v的是如果不点击按钮,就没有参数接受,就不会显示信息,否则则显示出来
2.组件通信方式2-自定义事件
自定义事件的概念:
当我们在Vue 3中需要让组件之间进行通信时,可以使用自定义事件。可以把自定义事件想象成人与人之间的交流,就像你和朋友之间通过说话来传递信息一样。
在Vue 3中,我们可以在一个组件中触发自定义事件,就像你向朋友说出一句话一样。你可以使用$emit
方法来触发事件,并传递一些数据作为事件的参数。
而在另一个组件中,你可以监听并响应这个自定义事件,就像是朋友听到你的话后做出回应。你可以使用$on
方法来监听事件,并在事件触发时执行相应的逻辑。
通过自定义事件,不同的组件可以进行灵活的交流和信息传递,就像是人们通过说话来相互沟通。这样就可以实现组件之间的解耦,让它们能够独立地进行通信。
概述:自定义事件常用于:子 => 父。
- 给子组件绑定了一个send-toy事件,只要点击就会调用saveToy,这就是自定义事件
- 如何触发send-toy事件呢?在子组件defineEmits声明中放入该事件
- 然后只要调用emit('send-toy')就可以触发了
Child.vue子组件代码:
<template>
<div class="child">
<h3>子组件</h3>
<h4>玩具:{{ toy }}</h4>
<button @click="emit('send-toy',toy)">测试</button>
</div>
</template>
<script setup lang="ts" name="Child">
import { ref } from "vue";
// 数据
let toy = ref('奥特曼')
// 声明事件
const emit = defineEmits(['send-toy'])
</script>
<style scoped>
.child{
margin-top: 10px;
background-color: rgb(76, 209, 76);
padding: 10px;
box-shadow: 0 0 10px black;
border-radius: 10px;
}
</style>
Father.vue父组件代码:
<template>
<div class="father">
<h3>父组件</h3>
<h4 v-show="toy">子给的玩具:{{ toy }}</h4>
<!-- 给子组件Child绑定事件 -->
<Child @send-toy="saveToy"/>
</div>
</template>
<script setup lang="ts" name="Father">
import Child from './Child.vue'
import { ref } from "vue";
// 数据
let toy = ref('')
// 用于保存传递过来的玩具
function saveToy(value:string){
console.log('saveToy',value)
toy.value = value
}
</script>
<style scoped>
.father{
background-color:rgb(165, 164, 164);
padding: 20px;
border-radius: 10px;
}
.father button{
margin-right: 5px;
}
</style>
3.组件通信方式3-mitt
mitt的概念:
"Mitt" 是一个小而灵活的JavaScript库,用于在应用程序中处理事件的订阅和发布。可以把它想象成一个事件调度中心,就像是一个小管家,帮助不同的组件之间进行信息传递和交流。
当我们需要在应用程序中的不同部分之间传递消息时,可以使用 "Mitt" 来管理这些消息。它提供了一种简单的方式来订阅事件和触发事件。
假设你有几个组件,它们需要相互通信。你可以创建一个 "Mitt" 的实例,就像是雇佣了一个小管家。然后,你可以让组件们通过 "Mitt" 的实例来订阅事件,就像是告诉小管家:“如果有人说了某个事件,告诉我一声”。
当某个组件需要发送一个事件时,它可以通过 "Mitt" 的实例来触发该事件,就像是对小管家说:“有人说了某个事件,快去告诉其他组件”。
然后,其他组件就会收到这个事件,并可以根据需要做出响应,就像是收到小管家传递的消息后采取相应的行动。
"Mitt" 的好处在于它的简洁性和灵活性。它不依赖于任何特定的框架或库,可以在各种环境中使用。它是一个轻量级的解决方案,但非常实用,可以帮助你构建松散耦合的应用程序,让组件之间的通信更加方便和可靠。
通过mitt可以任意组件传递数据
比如如下的A组件通过绑定emitter,然后B组件触发emitter就可以获得A组件数据,爽完。
$event.target
event是触发对象事件,target是发生三个事件的本体
双向绑定:页面和控制台绑定,即在那面修改信息,另外也会发生变化
概述:与消息订阅与发布(`pubsub`)功能类似,可以实现任意组件间通信。
1.安装mitt
npm i mitt
2.新建文件:src\utils\emitter.ts
emitter.ts文件代码:
// 引入mitt
import mitt from 'mitt'
// 调用mitt得到emitter,emitter能:绑定事件、触发事件
const emitter = mitt()
// 暴露emitter
export default emitter
3.接收数据的组件中:绑定事件、同时在销毁前解绑事件
4.组件通信方式4-v-model (重点)
v-model的概念:
当我们在构建一个交互性的网页或应用程序时,很常见的需求是让用户能够输入数据并与之进行交互。Vue中的 v-model
就是为了解决这个需求而设计的。
想象一下你有一个输入框,用户可以在里面输入文本。你希望能够获取用户输入的值,并将其保存到Vue实例中的一个数据属性中,以便后续使用。同时,你也希望当你在Vue实例中修改了这个数据属性的值时,输入框中的内容也能够自动更新。
这就是 v-model
的作用啦!它是Vue中的一个特殊指令,让你可以将输入框和Vue实例中的数据属性绑定在一起,实现双向的数据绑定。
当你在输入框中输入文本时,v-model
会自动将输入的值保存到Vue实例中的数据属性中。而当你在Vue实例中修改了这个数据属性的值时,v-model
会自动将最新的值显示在输入框中。
这就像是你和Vue实例之间建立了一条数据的通道:你可以通过输入框向Vue实例发送数据,同时也可以通过修改Vue实例中的数据属性来控制输入框的内容。
需要注意的是,v-model
并不仅限于输入框,还可以用于复选框、单选按钮等表单元素,甚至是自定义的组件。
4.1 v-model用在html标签上
1.双向绑定:控制台的信息能显示在页面上,同时哪边被修改了都可以同步。
2.控制台数据的信息呈现在页面上
3.在页面修改信息,控制台数据同步信息
这里是程序员要写的v-model的代码:
<input type="text" v-model="username">
其实这段代码的本质是:
<input type="text" :value="username" @input="username = (<HTMLInputElement>$event.target).value">
底层原理是动态的value值和input事件
这两段代码是等价的
补充:
为什么报警告呢?
输入担心不是html输入类型的元素。所有要<HTMLInputElement>来断言告诉它不用担心。
4.2. v-model用在组件标签上
我们先写一个组件:
该组件的作用是改变输入框的样式。
<template>
<input
>
<br>
<input
>
</template>
<script setup lang="ts" name="MyInput">
</script>
<style scoped>
input {
border: 2px solid black;
background-image: linear-gradient(45deg,red,yellow,green);
height: 30px;
font-size: 20px;
color: white;
}
</style>
然后在father.vue组件增添代码:
<MyInput v-model="username"/>
这段代码的本质:
<MyInput :modelValue="username" @update:modelValue="username = $event"/>
:modelvalue传过来"username",实现控制台数据呈现到页面上
@update:modelValue只是事件名,它绑定username这个事件
然后在MyInput.vue组件上修改:
1.用defineProps把modelvalue名接收了
2.呈现在input输入框中
上面两步是把控制台数据呈现到页面。
3. 用defineEmits把input事件里的update:modelValue接收了
4.同理,把它用在输入框中。
上面两步代码是在页面上修改数据时,也可以绑定控制台数据
其实MyInput.vue类似UI库里面的组件,我这么写是为了解释<MyInput v-model="username"/>底层逻辑的实现,实际开发中,直接写<MyInput v-model="username"/>就好了。
补充:
$event到底是啥? 啥时候能.target?
对于原生事件,$event就是事件对象 =====>能.target
对于自定义事件,$event就是触发事件时,所传递的数据 ==>不能target
MyInput.vue代码:
<template>
<input
type="text"
:value="modelValue"
@input="emit('update:modelValue',(<HTMLInputElement>$event.target).value)"
>
</template>
<script setup lang="ts" name="MyInput">
defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>
<style scoped>
input {
border: 2px solid black;
background-image: linear-gradient(45deg,red,yellow,green);
height: 30px;
font-size: 20px;
color: white;
}
</style>
Father.vue文件代码:
<template>
<div class="father">
<h3>父组件</h3>
<!-- v-model用在html标签上
1.程序员写的代码
<input type="text" v-model="username">
1.等价于下面这段代码
<input type="text" :value="username" @input="username = (<HTMLInputElement>$event.target).value"> -->
<!-- v-model用在组件标签上
2.程序员写的代码
<MyInput v-model="username"/>
2.等价于下面这段代码
<MyInput :modelValue="username" @update:modelValue="username = $event"/> -->
</div>
</template>
<script setup lang="ts" name="Father">
import { ref } from "vue";
import MyInput from './MyInput.vue'
// 数据
let username = ref('zhansgan')
</script>
<style scoped>
.father {
padding: 20px;
background-color: rgb(165, 164, 164);
border-radius: 10px;
}
</style>
4.3v-model的细节
<!-- 也可以更换value,例如改成abc-->
<MyInput v-model:ming="userName"/>
<!-- 上面代码的本质如下 -->
<MyInput :ming="uerName" @update:ming="userName = $event"/>
MyInput.vue文件中代码:
<template>
<input
type="text"
:value="modelValue"
@input="emit('update:ming',(<HTMLInputElement>$event.target).value)"
>
</template>ming
<script setup lang="ts" name="MyInput">
defineProps(['ming'])
const emit = defineEmits(['update:ming'])
</script>
<style scoped>
input {
border: 2px solid black;
background-image: linear-gradient(45deg,red,yellow,green);
height: 30px;
font-size: 20px;
color: white;
}
</style>
如果value
可以更换,那么就可以在组件标签上多次使用v-model
比如:
<MyInput v-model:ming="userName" v-model:mima="password/>