通信方式1 props
parent传child--只能传递非函数
// Parent.vue
<template>
<div class="parent">
<h3>parent</h3>
<h3>car: {{ car }}</h3>
<Child :car="car"/>
</div>
</template>
<script setup lang="ts">
import Child from './Child.vue';
import { ref } from 'vue';
let car = ref('a car')
</script>
Child.vue
<template>
<div class="child">
<h3>child</h3>
<h3>car from parent: {{ car }}</h3>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { defineProps } from 'vue';
defineProps(['car'])
</script>
child传parent--只能由parent传给child函数,child调用函数将数据给parent
// Parent.vue
<template>
<div class="parent">
<h3>parent</h3>
<h3>car: {{ car }}</h3>
<h3 v-show="toy">{{ toy }}</h3>
<Child :car="car" :sendToy="getToy"/>
</div>
</template>
<script setup lang="ts">
import Child from './Child.vue';
import { ref } from 'vue';
let car = ref('a car')
let toy = ref('')
function getToy(val) {
toy.value = val
}
</script>
// Child.vue
<template>
<div class="child">
<h3>child</h3>
<h3>car from parent: {{ car }}</h3>
<button @click="sendToy(toy)">sendToy</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { defineProps } from 'vue';
defineProps(['car','sendToy'])
let toy = ref('a bear')
</script>
通信方式2 自定义事件 event
- 只用于child传给parent
$event
用于接收点击事件- 事件名由多个单词组成时,使用类似
send-toy
的写法 const emit = defineEmits(['send-toy'])
是规范写法
// Parent.vue
<template>
<div class="parent">
<h3>parent</h3>
<h3 v-show="toy">{{ toy }}</h3>
<Child @send-toy="getToy"/>
</div>
</template>
<script setup lang="ts">
import Child from './Child.vue';
import { ref } from 'vue';
let toy = ref('')
function getToy(val) {
toy.value = val
}
</script>
// Child.vue
<template>
<div class="child">
<h3>child</h3>
<button @click="emit('send-toy',toy)">send-toy</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
let toy = ref('a kind toy')
const emit = defineEmits(['send-toy'])
</script>
通信方式3 mitt 实现任意组件之间数据互传
引入mitt
- npm i mitt
- 新建tools或utils文件夹,新建emitter.ts文档,用于引入mitt
import mitt from 'mitt'
// emitter可以绑定事件、触发事件
const emitter = mitt()
export default emitter
如何使用mitt
// 绑定事件
emitter.on('useMitt1',()=>{
console.log('useMitt1');
})
emitter.on('useMitt2',()=>{
console.log('useMitt2');
})
// 调用事件
setInterval(()=>{
emitter.emit('useMitt1')
emitter.emit('useMitt2')
},1000)
// 移除事件
setTimeout(()=>{
// emitter.off('useMitt1')
emitter.all.clear()
},3000)
使用mitt在两个组件间传递数据
接收数据的组件:绑定事件
传输数据的组件:触发事件
下例:child2传数据给child1
// Child1.vue
<template>
<div class="child">
<h3>child</h3>
<h3 v-show="toy">{{ toy }}</h3>
</div>
</template>
<script setup>
import { ref } from 'vue';
import emitter from '@/utils/emitter';
let toy = ref('')
emitter.on('send-toy',(val)=>{
toy.value = val
})
</script>
// Child2.vue
<template>
<div class="child">
<h3>child</h3>
<h3>child2's toy {{ toy }}</h3>
<button @click="emitter.emit('send-toy',toy)">send-toy</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
import emitter from '@/utils/emitter';
let toy = ref('a toy')
</script>
注意事项:最好在绑定事件后增加:当组件卸载时,解绑事件
emitter.on('send-toy',(val)=>{
toy.value = val
})
onUnmounted(()=>{
emitter.off('send-toy')
})
通信方式4 v-model
HTML标签使用v-model
// 直接使用v-model
<input type="text" v-model="username">
// v-model实现的底层逻辑
<input type="text" :value="username" @input="username = (<HTMLInputElement>$event.target).value">
组件标签使用v-model
<TestUIInput v-model="username"/>
// 底层逻辑
<TestUIInput :modelValue="username" @update:modelValue="username = $event"/>
组件标签可以使用v-model的前提是已经封装好了对应的逻辑
// TestUIInput.vue
<template>
<input type="text" :value="modelValue" @input="emit('update:modelValue',(<HTMLInputElement>$event.target).value)">
</template>
<script setup lang="ts" name="TestUIInput">
import { ref } from 'vue';
defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>
关于$event
对于原生事件,要使用$event.target
对于自定义事件,$event
是触发事件时传递的数据
更换v-model(类似于命名?),可以使用多个v-model
<TestUIInput v-model:uName="username" v-model:pwd="password"/>
// TestUIInput.vue
<template>
<input type="text" :value="uName" @input="emit('update:uName',(<HTMLInputElement>$event.target).value)">
<input type="text" :value="pwd" @input="emit('update:pwd',(<HTMLInputElement>$event.target).value)">
</template>
<script setup lang="ts" name="TestUIInput">
import { ref } from 'vue';
defineProps(['uName','pwd'])
const emit = defineEmits(['update:uName','update:pwd'])
</script>
通信方式5 attrs 实现祖辈传递孙辈
- 祖辈准备数据并传给child
<template>
<div class="parent">
<h3>parent</h3>
<Child :a="a" :b="b" :c="c" v-bind="{x:100,y:300}" :changeA="changeA"/>
</div>
</template>
- child不接收任何数据,并使用$attrs将数据传给grandchild
<template>
<div class="child">
<h3>child</h3>
<Grandchild v-bind="$attrs"/>
</div>
</template>
<script setup name="Child">
import Grandchild from './Grandchild.vue';
defineProps([''])
</script>
- grandchild接收所有数据
<template>
<div class="child">
<h3>Grandchild</h3>
<h4>{{ a }}</h4>
<h4>{{ b }}</h4>
<h4>{{ c }}</h4>
<h4>{{ x }}</h4>
<h4>{{ y }}</h4>
<button @click="changeA(b)">changeA</button>
</div>
</template>
<script setup name="Grandchild">
defineProps(['a','b','c','x','y','changeA'])
</script>
通信方式6 $refs 实现parent影响child $parent 实现child影响parent
使用ref,parent修改单个child的数据
Parent.vue
<template>
<div class="parent">
<h3>parent</h3>
<button @click="changeToy">change toy</button>
<Child1 ref="c1"/>
</div>
</template>
<script setup lang="ts">
import Child1 from './Child1.vue';
import { ref } from 'vue';
let c1 = ref()
function changeToy(){
c1.value.toy = 'another toy'
}
</script>
Child.vue
<template>
<div class="child">
<h3>child</h3>
<h3>{{ toy }}</h3>
</div>
</template>
<script setup>
import { onUnmounted, ref } from 'vue';
let toy = ref('toytoy')
let book = ref(6)
defineExpose({toy,book})
使用$refs,parent修改多个child的数据
Parent.vue
<template>
<div class="parent">
<h3>parent</h3>
<button @click="addBook($refs)">add book</button>
<Child1 ref="c1"/>
<Child2 ref="c2"/>
</div>
</template>
<script setup lang="ts">
import Child1 from './Child1.vue';
import Child2 from './Child2.vue';
import { ref } from 'vue';
let c1 = ref()
let c2 = ref()
function addBook(refs){
for (const key in refs) {
refs[key].book += 4
}
}
</script>
Child1.vue
<template>
<div class="child">
<h3>{{ book }}</h3>
</div>
</template>
<script setup>
import { onUnmounted, ref } from 'vue';
let book = ref(9)
defineExpose({book})
</script>
Child2.vue
<template>
<div class="child">
<h3>{{ book }}</h3>
</div>
</template>
<script setup>
import { onUnmounted, ref } from 'vue';
let book = ref(9)
defineExpose({book})
</script>
使用$parent,child修改parent的数据
Parent.vue
<template>
<div class="parent">
<h3>parent</h3>
<h3>house {{ house }}</h3>
<Child1 ref="c1"/>
<Child2 ref="c2"/>
</div>
</template>
<script setup lang="ts">
import Child1 from './Child1.vue';
import Child2 from './Child2.vue';
import { ref } from 'vue';
let house = ref(5)
defineExpose({house})
</script>
Child.vue
<template>
<div class="child">
<h3>child</h3>
<button @click="minusHouse($parent)">minusHouse</button>
</div>
</template>
<script setup>
function minusHouse(parent){
parent.house -= 1
}
</script>
通信方式7 provide inject 实现parent和所有后代之间的通信
Parent.vue
<template>
<div class="parent">
<h3>parent</h3>
<h3>money {{ money }}</h3>
<ChildOrGrandchild/>
</div>
</template>
<script setup lang="ts">
import ChildOrGrandchild from './ChildOrGrandchild.vue';
import { provide, ref } from 'vue';
let money = ref(20000)
function changeMoney(val){
money.value -= val
}
provide('moneyContext',{money,changeMoney})
</script>
ChildOrGrandchild.vue
<template>
<div class="child">
<h3>ChildOrGrandchild</h3>
<h3>money: {{ money }}</h3>
<button @click="changeMoney(8)">changeMoney</button>
</div>
</template>
<script setup name="Child">
import { inject } from 'vue'
let {money,changeMoney} = inject('moneyContext','default')
</script>
通信方式8 pinia 参考此链接
通信方式9 slot
默认插槽
在组件标签内填写想要插入的元素即可
// Parent.vue
<template>
<div class="parent">
<h3>parent</h3>
<div class="d-flex justify-content-around">
<Child title="标题1">
<ul>
<li v-for="item in list" :id="item.id">{{ item.name }}</li>
</ul>
</Child>
<Child title="标题2">
<img :src="imgUrl" alt="">
</Child>
<Child title="标题3">
<video :src="videoUrl" controls></video>
</Child>
</div>
</div>
</template>
// Child.vue
<template>
<div class="child">
<h5 class="bg-primary text-light">{{ title }}</h5>
<slot>default</slot>
</div>
</template>
具名插槽
- slot标签增加name属性命名
- v-slot:name 只能用在组件标签或者template标签
// Parent.vue
<template>
<div class="parent">
<h3>parent</h3>
<div class="d-flex justify-content-around">
<Child v-slot:s2>
<h5 class="bg-primary text-light">标题1</h5>
<ul>
<li v-for="item in list" :id="item.id">{{ item.name }}</li>
</ul>
</Child>
<Child>
<template v-slot:s1>
<h5 class="bg-primary text-light">标题2</h5>
</template>
<template v-slot:s2>
<img :src="imgUrl" alt="">
</template>
</Child>
<Child v-slot:s1 title="标题3">
<video :src="videoUrl" controls></video>
</Child>
</div>
</div>
</template>
// Child.vue
<template>
<div class="child">
<slot name="s1">default1</slot>
<slot name="s2">default2</slot>
</div>
</template>
作用域插槽
- 数据在child组件
- 使用数据的结构在parent组件
- child组件在使用slot的时候将数据传出去
// Child.vue
<template>
<div class="child">
<slot name="s1" :games="games" x="aaa" y="bbb"></slot>
<slot :games="games"></slot>
</div>
</template>
<script setup name="Child">
import { reactive } from 'vue';
let games = reactive([
{name:'game1',id:'lll01'},
{name:'game2',id:'lll02'},
{name:'game3',id:'lll03'},
{name:'game4',id:'lll04'},
])
</script>
// Parent.vue
<template>
<div class="parent">
<h3>parent</h3>
<div class="d-flex justify-content-around">
<Child v-slot:s1="params">
<h5 class="bg-primary text-light">{{ params }}</h5>
</Child>
<Child v-slot="params">
<ol>
<li v-for="item in params.games" :id="item.id">{{ item.name }}</li>
</ol>
</Child>
<Child v-slot:s1="params" title="标题3">
<h2 v-for="item in params.games" :id="item.id">{{ item.name }}</h2>
</Child>
</div>
</div>
</template>
<script setup lang="ts">
import Child from './Child.vue';
</script>
标签:vue,parent,学习,toy,014,child,import,ref
From: https://www.cnblogs.com/ayubene/p/18097551