Vue3 – 组件间数据传输的几种方法
简介
讲解Vue3中几种组件间数据传递的方法。
prop
porp 参数传递,可适用于父组件传递数据到子组件的场境。
注意点:
1.prop数据只读
2.defineProp 不需要引入vue
示例:
父组件中调用子组件
<Child prop1="prop" :prop2="prop"></Child>
子组件中接收父组件中传来的prop
方法一:直接接收
defineProps(['a', 'b']);
方法二:对象式接收,可定义类型
defineProps({
a: String,
b: String
});
方法三:对象接收对象式,可定义原始值,是否必须接收和类型
defineProps({
a: {
default: "a",
type: String,
required:true
},
b:{
default:"b",
type:String,
required:false
}
});
自定义事件 emit
一般用在子组件向父组件传递数据的情况
注意点:
1.defineEmits不需要引入
2.defineEmits返回值则是 emit 执行对象
3.当自定义事件的命名为“click”时,则原生的 @click 方法将会失效,会被当作普通的自定义事件看待
父组件中调用子组件
<Child @prop="propFn"></Child>
父组件中定义方法 propFn:
const propFn = () => {}
子组件中接收并调用自定义事件:
const emit = defineEmits(['prop','emit2'])
emit('prop',data...); // data 为传递的数据,将会传递给父组件的 propFn 方法作为参数,可传递多个
全局事件总线 $bus
$bus 全局事件总线一般应用在兄弟组件间的数据传递情况
注意点:
Vue3 中没有了 VM 和 VC 慨念,因此要使用 $bus 可以使用 插件 mitt
1.安装mitt插件
npm i mitt -S
2.引入 mitt 插件
import mitt from 'mitt'
const $bus = mitt();
引入mitt插件,mitt一个方法,方法执行会返回$bus对象
3.兄弟1引入 $bus ,并绑定接收数据的事件:
import $bus from 'bus' // bus 为导出 mitt 对象的 js 文件
// 在组件加载完成后即绑定事件
onMounted(()=>{
$bus.on('event', (prop)=>{
// 当兄弟2传递数据时,会执行这个方法,并能传递数据过来
console.log(prop);
})
})
4.兄弟2引入 $bus , 并使用 $bus 传递方法和参数,这个使兄弟1触发on方法
import $bus from 'bus' // bus 为导出 mitt 对象的 js 文件
// 兄弟2调用方法,把data传出去
$bus.emit('event',data);
useAttrs
Vue3 框架提供一个方法 useAttrs,它可以获取组件身上的属性与事件。
注意点:
1.useAttrs 实际上是父组件向子组件中传递的 prop 中的集合
2.useAttrs 与 props 互斥,当props 接收了父组件中的数据时,该数据就不会存在于 useAttrs
3.只有父组件传递的参数,而props中没有声明接收时,useAttrs 才会保存
4.useAttrs在Vue3中还会保存事件,如 @xxx 自定义事件,若不使用 defineEmits 接收该事件,则可在 useAttrs 中找到,如果接收了,则useAttrs 中将找不到。
引入 useAttrs 方法
import { useAttrs } from 'vue'
const attrs = useAttrs();
其执行返回值则是 attrs,保存了未被接收的 props 和 emits
子组件可将参数传递给孙组件:
<Child :title="attrs.title">
也可以使用v-bind的另一种语法,把所有的属性都传给孙组件:
<Child :="attrs">
语法说明:我们平时使用的 v-bind 的语法多数是如 :title="xxx" 的语法,实际上是 v-bind:title="xxx"的语法
但当我们希望把所有的属性都传给孙组件时,使用 v-bind="attrs" 就相当于把整个对象传给组件,类似于
v-bind="{title:"xxx",a:"aaa",b:"bbb"}" 这样,简写后,就是 :="attrs" 语法了。
发布订阅 pubsub
vuex
略,详见VueX相关文章
Pinia
略,详见Pinia相关文章
组件引用 ref 与 $parent
ref 可以获取真实的DOM节点,可以获取到子组件的实例VC
而$parent 可以在子组件内部获取到父组件的实例
ref
父组件使用与组件同名的 ref 方法获取该组件的VC实例,如下代码:
父组件调用子组件,并设置ref
<Child ref="son"></Child>
父组件使用 ref 获取 子组件的 VC
import { ref } from 'vue';
let son = ref(null);
通过获取到子组件实例,就可以获取到子组件的参数,但前提是,子组件的数据需要对外暴露:
子组件中需要对数据对外暴露
const name = "子组件中的数据"
子组件要对外暴露数据
defineExpose({
name
})
父组件中通过 ref 获取到数据之后,就可以直接读取子组件的数据了
let son = ref();
读属性数据
console.log( son.value.name.value )
写属性数据
son.value.name.value = "父组件改变的数据"
$parent
$parent 可以实现在子组件中,获取到它的父组件的属性数据,但前提是父组件也必须对外暴露
子组件获取 $parent
<button @click="handler($parent)">点击获取parent</button>
子组件中可以通过事件参数传入 $parent
const handler = ($parent) => {
console.log($parent)
}
注意:事件的传入参数名必须为“$parent” 才可以获得父组件实例
父组件中对外暴露属性数据
let father = ref("父组件数据")
defineExpose({
father
})
子组件中获取到 $parent 后就可对父组件中的数据进行读写操作
const handler = ($parent) => {
读父组件中的数据
console.log( $parent.value.father )
写父组件中的数据
$parent.value.father.value = "子组件写父组件数据"
}
插槽 slot
插槽分为默认插槽、具名插槽、作用域插槽
默认插槽
使用默认插槽,其名称使用默认 default
父组件中调用子组件
<Child> 父组件中传递数据给子 </Child>
子组件中使用 slot 进行占位
<template>
<slot><slot>
</template>
具名插槽
具名插槽指的是子组件中可以创建两个占位,而为了识别这两个占位都应该传递什么数据,这时这两个占位都应该设置名称,父组件中希望把数据传给哪一个占位,就使用名称进行定位
子组件中创建两个插槽占位
<template>
第一个占位名称为 a
<slot name="a"><slot>
第二个占位名称为 b
<slot name="b"><slot>
</template>
这样父组件可以通过名称把数据准确地投方到指定的占位插槽
中
父组件中分别传递不同数据给插槽
<Child>
<template v-slot="a">
这数据将传到a插槽中
</template>
<template #b>
通过使用简写 # 定义插槽名称
这数据将传到b插槽中
</template>
</Child>
作用域插槽
作用域插槽的作用是可以让子组件把数据回传给父组件使用。
子组件中创建作用域插槽,可以将数据回传给父组件
<li v-for="(item,index) in list">
<slot name="a" :row="item" :index="index"></slot>
</li>
说明:在子组件中接收来自父组件传过来的数组数据,而在子组件中对数组进行遍历,这时循环会产生多个插槽,作用域插槽就是可以把数组遍历后得到的携带不同数据的插槽传给父组件,使用 prop 的方式,这样在父组件中,就能接收到子组件遍历后传递的数据了。
父组件中使用作用域插槽传递过来的数据
<Child :list="data">
<template #a={row, index}>
<div>
可以在父组件中接收子组件的数据并组和结构后
再由插槽传给子组件使用 {{ row }} -- {{ index }}
</div>
</template>
</Child>
注意:作用域插槽也可以使用具名插槽的方法
provide 与 inject
provide 与 inject 可以实现隔代组件的数据传递,通常是祖组件传孙组件的隔代传递
provide 的作用是提供,inject的作用是接收数据注入作用
祖先组件提供数据
import { provide } from 'vue'
let data = ref("父组件的数据")
provide("token", data);
provide与inject 除了可以实现数据传递外,还支持数据修改,因为 provide 和 inject 所传递的是数据的内存数据
孙子组件中接收并使用数据
import { inject } from 'vue'
let data = inject("token")
读数据
console.log( data.value )
写数据
data.value = "孙组件修改数据"
v-model
Vue3 中的 v-model 除了可以双向绑定数据,实际上还能做父子数据传递
注意点:
v-model 实际上是 prop 和 emit 的合体,父传子组件时实际上使用的是 prop 方法把数据传给子组件,当子组件中同步数据时,实际上是隐式调用了 emit 方法。
举例一:通过 prop 和 emit 打配合实现父子间数据同步
// 父组件中使用 prop 传递数据,并使用自定义事件定义回调
<Child :prop="propData" @update:prop="propFn"></Child>
// 子组件使用 defineProp 接收数据 prop ,并使用 defineEmits 接收 update:prop自定义方法
const props = defineProp(['prop'])
const emits = defineEmits(['update:prop'])
// 当需要读取数据时,使用 props.prop 中的数据
console.log(props.prop);
// 当需要修改数据时,使用 emits 来传递数据给父组件
emits('update:prop',newData);
// 父组件中,监听方法,并修改数据
const propFn = (newData) => {
propData = newData;
}
v-model 实际上是隐式的帮我们创建了以上所有代码的执行过程,唯一我们要做的,是在父组件中,需要手动定义接收 emit 自定义方法来同步数据。
注意:v-model 隐式生成的 prop 名字一定是 modelValue , 而生成的 emit 方法名字一定是 @update:modelValue,即如下代码
// 父组件中使用 prop 传递数据,并使用自定义事件定义回调
<Child :modelValue="propData" @update:modelValue="handler"></Child>
// 子组件使用 defineProp 接收数据 prop ,并使用 defineEmits 接收 update:prop自定义方法
const props = defineProp(['modelValue'])
const emits = defineEmits(['update:modelValue'])
// 当需要读取数据时,使用 props.modelValue 中的数据
console.log(props.modelValue);
// 当需要修改数据时,使用 emits 来传递数据给父组件
emits('update:modelValue',newData);
// 父组件中,监听方法,并修改数据
const handler= (newData) => {
propData = newData;
}
绑定多个 v-model
Vue3 中支持多个 v-model 的绑定,当绑定多个 v-model 时,则表示子组件传入多个 prop,如下
// 父组件在子组件里使用了多个 v-model
<Child v-model:pageNo="2" v-model:pageSize="10"></Child>
这在子组件中,相当于接收了两个 prop 参数,分别为 "pageNo" 和 "pageSize",可通过 defineProps获取
// 子组件中可通过 defineProps() 来获取 v-model 传过来的参数
const props = defineProps(['pageNo','pageSize'])
而子组件中,同时也绑定了两个 emit 自定义事件,同样分别为 "@update:pageNo" 和 "@update:pageSize"
// 子组件中可通过 defineEmits() 来获取 v-model 传过来的emit事件
const emits = defineEmits(['update:pageNo','update:pageSize'])
子组件可通过调用方法达到把数据传到父组件的效果:
const emits = defineEmits(['update:pageNo','update:pageSize'])
// 子组件调用自定义事件,实现数据回传给父组件
emits('update:pageNo',newData);
emits('update:pageSize',newData);
注意:父组件中会自动同步数据到相应的数据对象中,即使父组件显式声明 @update:xxx 的回调方法,除了该回调方法会被执行外,依然还会同步父组件的对应数据。
共有 0 条评论