当前位置: 首页 > news >正文

vue学习八

十七 组件通信方式

1 props

父传子

//父组件
<script setup>
    //book来源省略
    import Subview1 from './Subview1.vue';
    function updatebook(updatetimes){
        book.value.updatetimes = updatetimes
    }
</script>
<template>
    <Subview1 :book="book" :updatebook=updatebook></Subview1>
</template>

//子组件
<script setup>
    import { ref } from 'vue'
    let updatetimes = ref(0)//修改次数
    defineProps(['book','updatebook'])
</script>
<template>
    <p>{{ book }}</p>
    <p><button @click="updatebook(updatetimes)">updateprice</button> </p>
</template>

 点击updateprice前

 点击updateprice后

例子中列表来源于mockjs,点击详细这则设置book为选中的列表中对应位置的值,图里选中的是第一个。

点击详细后,父组件将book传给子组件。

点击updateprice后,子组件将其定义的变量updatetimes传给父组件。

父组件传给子组件,依靠子组件 defineProps设置的变量。

子组件传给父组件,依靠子组件 defineProps设置的方法,该方法体在父组件中设置。

2 自定义事件

用于子向父传数据。

子组件使用defineEmits定义事件,使用emit()触发事件。

父组件对于定义的事件绑定相应方法

//父组件
<script setup>
//list book 来源省略
function updatebook2(book){
    //修改父组件list中的值
    list[list_index]= book
} 
</script>
<template>
    <Subview1 :book="book" :updatebook="updatebook" @update:book="updatebook2"></Subview1> 
</template>


//子组件
<script setup>
import { ref } from 'vue'
let updatetimes = ref(0)//修改次数
defineProps(['book','updatebook'])
//定义事件
let emit = defineEmits(['update:book'])
//修改价格
function updateprice(book){
    book.price = book.price + 1
    updatetimes.value++
    book.updatetimes = updatetimes
    emit('update:book',book)
}
</script>
<template>
    <p><button @click="updateprice(book)">updateprice</button> </p>
</template>

 点击修改前

 点击两次修改后

子组件定义事件,可随时设置事件调用。

父组件设置子组件事件绑定的对应回调。

子组件事件触发后,会执行父类定义的回调。

官方推荐:

父组件中可以使用 kebab-case 形式来监听。

实际就是例子中update:book变成update-book

组件中自定义事件也支持修饰符。

组件触发的事件没有冒泡机制,只能监听直接子组件触发的事件,即平级的和跨层的emit监听不到。

3 mitt

可实现任意组件通信。

流程:定义事件并绑定操作->中间件做事件管理->触发事件触发对应操作。

定义:项目名\src\utils\emitter.js

import mitt from "mitt"
const emitter = mitt()

export default emitter

父组件

<script setup> 
……
import Subview1 from './Subview1.vue';
import Subview2 from './Subview2.vue';
……
function updatebook2(book){
    list[list_index]= book
} 
</script>
<template>
    <div class="main">
        <h1>View 10</h1>
        <h3>books</h3>
        <div v-for="item in list" :key="item.id" class="book_item">
            <div class="img"> 
                <img :src="item.cover" alt="item.name">
            </div>
            <div class="info">   
                <p>name:{{ item.name }}</p>
                <p>price:{{item.price}}</p>
            </div>
            <div>
                <button @click="showbook(item.id)">详细</button>  
            </div>
        </div>
        <div class="sub" v-show="book">
            <Subview1 :book="book" :updatebook="updatebook" @update:book="updatebook2"></Subview1> 
        </div>
        <div class="sub">
            <Subview2></Subview2>
        </div>
    </div>
</template>  

 Subview1

<script setup>
import { ref } from 'vue'
import mitt from '@/utils/emitter.js';
let updatetimes = ref(0)//修改次数
defineProps(['book','updatebook'])
let emit = defineEmits(['update:book'])
//修改价格
function updateprice(book){
    book.price = book.price + 1
    updatetimes.value++
    book.updatetimes = updatetimes
    emit('update:book',book)
}
</script>
<template>
    <div >
        <h2>Subview1</h2>
        <p>{{ book }}</p>
        <p>updatetimes:{{updatetimes}}</p>
        <!-- <p><button @click="updatebook(updatetimes)">updateprice</button> </p> -->
        <p>
            <button @click="updateprice(book)">updateprice</button>
            &nbsp;
            <button @click="mitt.emit('buy-book',book)">buybook</button> 
            
        </p>
    </div>
</template>  
<style scoped>
</style>

Subview2

<script setup>
import { reactive } from 'vue'
import mitt from '@/utils/emitter.js';
let buybooks =reactive([])
mitt.on('buy-book',(book)=>{
    let hasbook =false
    for (var value of buybooks) {
        if(value.id===book.id){
            value.num++;
            hasbook = true;
            break;
        }
    }
    if(!hasbook){
        book.num=1;
        buybooks.push(book)
    }
})

</script>
<template>
    <div >
        <h2>Subview2</h2>
        <div v-show="buybooks.length>0">
            <p>buy books</p>
            <div v-for="(item,key) in buybooks" :key="key">
                <!-- {{key}} -->
                {{ item.name }}&nbsp;&nbsp;{{ item.num }}x{{item.price}} = {{item.price*item.num}} 
            </div>
        </div>
        
    </div> 
</template>

 根据样例,父类展示两个子类,子类中引用mitt定义文件。

Subview1中触发事件,需要defineEmits定义事件名,使用mitte.emit(时间名,参数)调用。

Subview2中接收事件,使用mitt.on(事件名,回调)。

调用mitt.on()后最好再调用mitt.off()进行解绑。

onUnmounted(()=>{
    mitt.off(事件名)
})

 上图为点击一次buybook效果。

 上图为点击一次updateprice之后再点击一次buybook效果。

4 v-model

用与变量双向绑定。

<script setup>
……
let book =ref(false)
……
function inputaction($event){
    book.value.name = $event.target.value
}
function inputaction1(a,$event){
    book.value.name = a+"_"+$event.target.value
}
</script>
<template>
    <div>
        book:<input type="text" :value="book.name" @input="inputaction">
        <br>
       book: <input type="text" v-model="book.name">
        <br>
        book:<input type="text" :value="book.name" @input="inputaction1(1,$event)">
    </div>
</template>

 例子中input两种帮定效果一样。

原始方式就是绑定变量(此时是单向绑定)之后,再绑定方法(此时是双向绑定)用于修改变量。

v-model命令直接可以双向绑定变量。

绑定事件不设置参数时参数默认$event,设置参数时传递$event需要写在参数列表中。

使用在组件中就是实现组件通信,跨组件变量的双向绑定。

若不使用v-model就是在组件中设置props和emits:

//父组件
<Subview :bookname="book.name" @update:bookname="book.name=$event"></Subview>

//子组件
defineProps(["bookname"])
defineEmits(["update:bookname"])


<input type="text" :value="bookname" @input="$emit('update:bookname',$event.target.value)">

 使用v-model后:

//父类
<Subview2 v-model="book.price"></Subview2>

//子类
defineProps(["modelValue"])
defineEmits(["update:modelValue"])

bookprice:<input type="text" :value="modelValue" @input="$emit('update:modelValue',$event.target.value)">

 子组件中modelValue和update:modelValue是固定的。

5.definemodel

vue3.4提供的宏definemodel,可以简化v-model中组件的代码。

父组件

<Subview1 v-model="book.name" v-model:bookprice="book.price"></Subview1> 

子组件

let [bookname] = defineModel()  
let bookprice = defineModel("bookprice")
function changename($event){
    bookprice.value =$event.target.value
} 



<input type="text" :value="bookprice" @input="changename">

 父附件可以设置多个v-model,子组件可以解构defineModel或者直接使用。

defineModel生命一个model prop,所以修改方法和prop一样。

6 $attrs

用于当前组件的父组件和子组件传数据。

父组件传给子组件的参数中,若子组件未接收,则存在$attrs。

可以将$attrs传给子组件,子组件再接收。

父类

let test=ref("test")

<Subview2 :bookname="book.name" :test="test"></Subview2>

子类

//Subview2

defineProps(["bookname"])


<Subview3 v-bind="$attrs"></Subview3>    


//Subview3

<script setup> 
defineProps(["test"])
</script>

<template>
    <h3>Subview3</h3>
    <p>
        test: {{ test }}
    </p>
</template>

6 $refs

用于获取和修改子组件暴露的数据。

子组件需要使用defineExpose暴露变量。

父组件在子组件上设置ref,用于$refs获取。

父组件

function add_updatetimes(values){
    console.log(values)
    console.log(values['sub1'].updatetimes)
    values['sub1'].updatetimes+=1
}



<button @click="add_updatetimes($refs)">add updatetimes</button>

<Subview1 ref="sub1"></Subview1> 

子组件

let updatetimes = ref(0)
defineExpose({updatetimes})


<p>updatetimes:{{updatetimes}}</p>

 console输出

 

点击add updatetimes 后

 一个父组件可以有多个子组件。

7 $parent

用于子组件获取和修改父组件暴露的数据。

父组件暴露数据后,子组件通过$parent获取和修改。

父组件

let test=ref("test")
defineExpose({test})

 子组件

function updatetest(parent){
    console.log(parent)
    parent.test = "subview1"
}

<button @click="updatetest($parent)">update test</button> 

 console输出

一个子组件只能由一个父组件,即只能获取最直接的调用其的父附件。

跨组件调用$parent获取不到对应数据。

8 inject&provide

父组件通过provide向之后的子组件传递数据,可跨组件。

子组件通过inject获取父组件传递的数据,可跨组件。

父组件在设置provide,可设置方法,在子组件中可通过改方法向父组件通讯。

父组件

let test1=ref("test1")
function changetest(value){
    test1.value+="."+value
}
provide("view10_test",{test1,changetest})

子组件

let {test1,changetest} = inject("view10_test","默认值")




<p>
    test1: {{ test1 }}
</p>
 <button @click="changetest('new test')" >change test</button>

点击change test后

相关文章:

  • Apache Tomcat漏洞公开发布仅30小时后即遭利用
  • 技术进阶:Open WebUI与Ollama的跨平台整合秘籍
  • 一、人工智能开发入门
  • 贪心算法(9)(java)最优除法
  • Apache Paimon 在抖音集团多场景中的优化实践
  • 昆仑万维开源R1V:38B参数多模态推理模型开启AI新纪元
  • 网络编程中的黏包和半包问题
  • [AI]实现简易AI Agent — — Transformers库
  • HTTP+DNS综合实验
  • Java面试黄金宝典3
  • 链表操作:分区与回文判断
  • (超详细) ETL工具之Kettle
  • ai应用开发代码
  • QT日志级别设置
  • xlsx.utils.json_to_sheet函数详解
  • web第六次
  • 在 STM32F7 系列微控制器中,使用定时器(如 TIM10)实现 10ms 中断,并在中断服务函数中调用 ProRelay() 函数
  • 配置集群-日志聚集操作
  • Flutter IconButton完全指南:高效使用与性能优化秘籍
  • 只是“更轻更薄”?不!遨游三防平板还选择“更强更韧”
  • 30天内三访中国,宝马董事长:没有一家公司可以在全球价值链外独立运行
  • 马上评丨从东方红一号到神二十,中国航天步履不停
  • 魔都眼·上海车展④|奔驰宝马保时捷……全球豪车扎堆首秀
  • 2025一季度,上海有两把刷子
  • “雷公”起诉人贩子王浩文案开庭:庭审前手写道歉信,庭审中不承认拐走川川
  • 沂水县委书记陈士贤,跨市履新泰安市委常委、组织部部长