【Vue3基础】Vue3 组件间通信

Vue3中组件通信

1.方式1:defineProps

defineProps通常用于在单文件组件中,父组件向子组件传递数据。

1.1 示例

父组件 blog.vue

// Blog.vue
<template>
	<Aritice :title="AriticeItem.title" :info="AriticeItem.info" :author="AriticeItem.author">
	 <!-- 使用v-bind ( : ) ,父组件将值绑定到子组件上 -->
    </Aritice>
</template>
<script setup>
	import { reactive } from 'vue';
	import Article from '@/components/Article.vue';
	const AriticeItem = reactive({
		title: '文章标题',
	    info: '文章内容',
	    author: 'X',
	  });
</script>

子组件 Article.vue

//Article.vue
<template>
	<section>
  		<p>{{ title }}</p>
    	<p>{{ info }}</p>
    	<p>{{ author }}</p>
	</section>
</template>
<script setup>
	const props = defineProps({
		title: String,
		info: String,
		author: String,
	});
    // 对象形式声明 props
	// 等价于以字符串数组声明 props
	//const props = defineProps(['title', 'info', 'author']);
    ...
	// 在setup中使用时则直接 props.title 即可。
    const title1 = props.title;
</script>
<style></style>
1.2 补充说明

首先是关于声明方式

对象形式声明时,key为对应的props名称,而value则可以有多种写法。在上面的例子中,value的值为该props对应的数据类型。

const props = defineProps({
	title: String,
	...
})

而实际上,value还可以为一个对象,其中对props的相关信息进行了设定,包括:

  • type — props的类型,如果可能存在多个类型,则可以写成数组形式,例如:[String,Number]
  • default — 该props的默认值
  • require — 是否为必传项,默认为false

以上面的title为例,其实际上可以写成:

const props = defineProps({
	title: {
        type: String,
        default: '111',
        require: false,
    }
})

字符串数组形式声明时,数组的每一项字面量就为prop对应的名称。

const props = defineProps(['title', 'info', 'author']);

其次,关于props的使用

template 模版中,直接使用defineProps()中声明的名称。

setup 中,只需要直接props.xxx就可以获取到该props对应的值。

另外,还需要注意:

  1. props一般是单向的,所以在子组件中不应该对 props 进行修改。如果需要在子组件中对props进行处理,应当使用ref对props进行处理后,再进行修改。

    const formatTitle = ref(props.title)
    //这样,子组件中后续对 formatTitle 的修改,就与原props.title 无关联了
    
  2. 当父组件传递的为一个对象时,例如对于上面的例子:

    <Aritice v-bind="AriticeItem"></Aritice>
    <!-- 也可 <Aritice :="AriticeItem"></Aritice>-->
    

    此时,子组件中的声明不需要改变,但是需要将该对象中将会用到的属性值通过defineProps声明出来。

  3. 在向子组件传递数据时,注意组件上用v-bind绑定的值不要重复,也不要与组件本身的属性重名!

2.defineEmits

同样地,defineEmits也常用于单文件组件的组件间通信,但其多用于子组件向父组件传递数据。

2.1 示例

父组件

<template>
  <section class="parent">
    <childVue :num="nums" @increase="handleIncrease"></childVue>
  </section>
</template>

<script setup>
  import childVue from './child.vue';
  import { ref } from 'vue';
  const nums = ref(0);
  const handleIncrease = () => {
    nums.value++;
  };
</script>

子组件

<template>
  <section class="box" @click="handelClick">{{ num }}</section>
</template>

<script setup>
  const emits = defineEmits(['increase']);
  const handelClick = () => {
    emits('increase');
  };
</script>

其效果实际上就是点击后,子组件中的value加一。

下面对该示例进行说明。

首先,在父组件中,向子组件传递了一个num的props用于展示数据。

同时,在父组件中定义了一个名为 increase 的监听事件以及事件触发后的处理函数(即把num加一)。

而在子组件中,添加了一个点击事件,在该事件的处理函数中,通过对象emit触发了父组件的 increase 监听事件,导致num加一。

2.2 具体用法

defineEmitsdefineProps一样,不需要引入,可以在 setup 函数中直接使用。

defineEmits可以用于声明对父组件上的一个或多个监听事件的触发,并返回一个触发函数对象emit

下面是声明方式:

const emits = defineEmits(['increase']);
// 声明多个触发
const emits = defineEmits(['increase', 'emit2', 'emit3']);

如何使用触发?

// 要触发哪个监听事件,就传入对应的事件名
emits('increase');
emits('emit2');
2.3 说明

关于emits()的参数:

  • 第一个参数,是监听事件的字面量(即名称)。
  • 第二个参数为事件传递的参数。如果该事件有多个参数,第二个参数建议用对象的形式传递。

例如:

//子组件发送,传递两个参数
emits('increase', {params1:'1',params2:'2'});
...
//父组件监听事件的处理函数
const handleIncrease = (params) => {
	console.log(params.params1, params.params2);//{'1','2'}
	nums.value += datas;
};

3.$ref + defineExpose

这种方法通常用于实现父子组件之间的通信。

在 vue2 中,有时候想访问$refs绑定的组件的属性或者方法,我们会使用 $refs

但在 vue3 中,由于在 setup 中无法访问到 this,所以这种方式是不适用的!那么为了能够使用 $refs,我们需要借助一个方法:getCurrentInstance()。该对象返回当前的实例对象,但在使用该方法时,需要注意:

  • 不要将该函数当作一个 optionsApi 来获取 this
  • 该方法仅在 setup生命周期函数中有效,在方法中是无效的。
3.1 实例

父组件

<!-- 父组件 -->
<template>
    <div>
        <Child ref="child"></Child>
        <button @click="show">Show Child Message</button>
    </div>
</template>


<script setup>

import { getCurrentInstance } from '@vue/runtime-core';
import Child from './Child.vue';

const currentInstance = getCurrentInstance()

function show() {
    currentInstance.ctx.$refs.child.alertMessage()
}

</script>

子组件

<!-- 子组件 -->
<template>
    <div>
        <h1>{{ message }}</h1>
    </div>
</template>

<script setup>
import { ref } from '@vue/reactivity';

let message = ref('我是子元素').value

const alertMessage = function () {
    alert(message)
}

//向外暴露指定属性
defineExpose({
    message,
    alertMessage
})
</script>

这里需要注意的是:在使用<script setup>语法糖时,组件是默认关闭的,即:在父组件中通过$refs来访问子组件中的值在子组件中通过$parents来访问父组件中的值时,都是取不到的!必须通过defineExpose暴露对应值后,才可以被访问到。

3.2 说明

实际上,在开发中,上面使用$refs的方式并不多见,因为其必须要依靠getCurrentInstance()方法。

使用较多的是下面这种方法:

<template>
<div><input type="text" ref="txt1" value="hello" /></div>
</template>

<script>
import { defineComponent, ref } from "vue";

export default defineComponent({
  setup() {
  	const txt1 = ref();

	const test = () => {
		console.log(txt1.value.value);
	}
	
	return {
		txt1,//一定不要忘记在return中返回给外部,因为这里相当于是声明一个响应式对象txt1,然后将其返回,并在模板中将其绑定为DOM元素的ref。
		test
	}
  }
});
</script>

或者可以这样:

<template>
<div><input type="text" ref="txt1" value="hello" /></div>
</template>

<script setup>
import { defineComponent, ref } from "vue";

const txt1 = ref();
const test = () => {
	console.log(txt1.value.value);
}
</script>

即在<script setup>语法糖中,可以不用返回,因为其中的值为自动返回。

接下来就可以通过声明的响应式对象来使用对应组件中通过defineExpose暴露出来的属性了!

4.其它方法

vue3中除了以上三种组件间通信的方式,还有其它可以实现组件间通信的方法:

  • v-model
  • 依赖注入 provide / inject
  • eventBus
  • vuex

其中,v-model 并不能严格成为数据的传递方式,其实只是减少了代码量。

而关于 eventBus,虽然 vue3 中将其移除,但仍然可以通过第三方插件来使用。

还有 provide / inject,关于这个我会有单独的一篇文章进行介绍,因为我自身也没怎么接触过。

最后就是 vuex ,vue 的状态管理工具,基本上是项目里面必然会用到的,这里就不说了。

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值