我是靠谱客的博主 鲜艳衬衫,这篇文章主要介绍Vue组件化开发--组件通信方式-父传子、子传父、非父子组件传值,现在分享给大家,希望可以做个参考。

一、概述

以脚手架搭建的Vue项目为笔记背景。

如果将所有的代码逻辑全部放到一个组件中,代码是非常的臃肿和难以维护的。

并且在真实开发中,可能会有更多的内容和代码逻辑,对于扩展性和可维护性来说都是非常不友好的。

所以开发者,我们会把一个大功能进行拆分,拆分成一个个功能的小组件。

那组件多了,就必然涉及到组件之间的数据传递。

二、组件通信-父传子

2.1 props属性的作用和使用场景

父组件有一些数据,需要子组件来进行展示。这种情况可以通过props组合式API来完成组件之间的通信。

props可以在当前组件上注册一些自定义的attribute(属性)。

父组件给这些attribute赋值,子组件通过attribute的名称获取到对应的值。

2.2 props有两种常见的用法

  • 字符串数组,数组中的字符串就是attribute的名称

    复制代码
    1
    2
    props: ['name', 'age']
  • 对象类型,对象类型可以在指定attribute名称时,指定它的类型、是否是必须的、默认值等等

    复制代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    props: { name: { type: String, // 需要传递的数据类型 required: true, // 是否必须传递,不传递的话报错 default: "张三" // 属性默认值 }, // 一般required和default只选其一配置即可 // 对象类型的默认值,必须是一个函数,使用函数返回值来确定默认值 friend: { type: Object, default() { return { name: "james" } } }, hobbies: { type: Array, default: () => ["篮球", "rap", "唱跳"] }, }

如何选择?

数组类型它对传入的attribute的名称,并不能对其进行任何限制吗,而对象的写法则让props变得更加完善。

当使用对象语法的时候,我们可以对传入的内容作出很多限制:

  • 指定传入的attribute的类型;
  • 指定传入的attribute是否是必传的;
  • 指定没有传入时,attribute的默认值;

这些限制可以让程序更加严谨。通常都会选用这种方式去定义props的相关属性。

注意点:

  • 一般requireddefault只选其一配置即可
  • 对象类型的默认值,必须是一个函数,使用函数返回值来确定默认值

2.3 props属性的type类型支持

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol

2.5 props属性命名问题

由于HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。

因此使用 DOM 模板时,驼峰命名法的 prop 名可以使用其等价的 短横线分隔命名方式来使用。

也可以直接使用驼峰命名法来使用。

定义时:

复制代码
1
2
3
4
5
6
7
8
props: { showMessage: { type: String, default: "默认showMessage的值" } }

使用时:

复制代码
1
2
3
4
<show-info :age="100" showMessage="我的个人信息"/> <show-info :age="100" show-message="我的个人信息"/>

更推荐使用时,用短横线分隔的方式来使用。

2.6 关于非Prop的Attribute

当我传递给一个组件某个属性,该属性并没有定义对应的props或者emits时,就称之为 非propsAttribute

常见的包括classstyleid等属性 。

这时有两种情况:

  • 当组件有单个根节点时,非propsAttribute将自动添加到根节点的标签属性中。

  • 如果组件有多个根节点,那么会报警告,必须手动的指定要绑定到哪一个属性上。

    • 手动指定的方式是使用$attrs来获取非propsAttribute

      复制代码
      1
      2
      3
      4
      5
      6
      7
      <template> <div class="infos"> <h2 :class="$attrs.class">姓名: {{ name }}</h2> <h2>年龄: {{ age }}</h2> </div> </template>

上面的情况就叫做属性继承。

如果不希望组件的根元素继承attribute,可以在组件中设置 :

复制代码
1
2
3
4
export default { inheritAttrs: false }

上面设置可以禁用Attribute继承和多根节点的方式。

2.7 代码示例

1)父组件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<template> <!-- 展示个人信息的组件 --> <!-- 如果当前的属性是一个非prop的attribute, 那么该属性会默认添加到子组件的根元素上 --> <show-info name="jack" :age="25" :height="1.78" address="广州市" abc="cba" class="active" /> </template> <script> // 导入子组件 import ShowInfo from './ShowInfo.vue' export default { // 局部注册子组件 components: { ShowInfo } } </script> <style scoped> </style>

2)子组件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<template> <div class="infos"> <!-- 获取继承而来的class属性 --> <h2 :class="$attrs.class">姓名: {{ name }}</h2> <h2>年龄: {{ age }}</h2> <h2>身高: {{ height }}</h2> <h2>Message: {{ showMessage }}</h2> </div> <!-- v-bind="$attrs 一次性把所有继承而来的属性全部绑定到当前元素上面 --> <div class="others" v-bind="$attrs"></div> </template> <script> export default { //inheritAttrs: false, // 设置不允许属性继承 props: { // props对象语法定义属性 name: { type: String, default: "我是默认name" }, age: { type: Number, required: true }, height: { type: Number, default: 1.75 }, // 对象类型写默认值时, 需要编写default的函数, 函数返回默认值 friend: { type: Object, default() { return { name: "james" } } }, hobbies: { type: Array, default: () => ["篮球", "rap", "唱跳"] }, showMessage: { type: String, default: "我是showMessage" } } } </script> <style scoped> </style>

三、组件通信-子传父

3.1 使用场景

  • 当子组件有一些事件被触发时,比如在组件中发生了点击,父组件需要作出一定的响应;

  • 子组件有一些内容想要传递给父组件的时候;

3.2 操作步骤

  • 在子组件中定义好在某些情况下触发的事件名称(自定义事件);

  • 在父组件中以v-on的方式传入要监听的事件名称,并且绑定到对应的方法中;

  • 在子组件中发生某个事件的时候,根据事件名称触发对应的事件;

3.3 自定义事件实现子向父传递

子组件:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template> <div class="add"> <button @click="btnClick(1)">+1</button> <button @click="btnClick(5)">+5</button> <button @click="btnClick(10)">+10</button> </div> </template> <script> export default { methods: { btnClick(count) { // 当btnClick方法被调用时,子组件会发出去一个自定义事件 // 第一个参数自定义的事件名称 // 第二个参数是传递的参数 this.$emit("add", count) } } } </script>

父组件:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<template> <div class="app"> <h2>当前计数: {{ counter }}</h2> <!-- 并且监听组件add-counter内部的add事件 --> <add-counter @add="addBtnClick"></add-counter> </div> </template> <script> //导入组件 import AddCounter from './AddCounter.vue' export default { // 注册组件 components: { AddCounter, }, data() { return { counter: 0 } }, methods: { // 子组件中add事件触发时,将调用此方法 addBtnClick(count) { this.counter += count }, } } </script>

简单来说:

  • 父组件内, 在子组件标签上添加 @自定义事件="父methods函数"

  • 子组件内, 恰当时机this.$emit('自定义事件名', 值)

4.3 关于Vue3.x对上面方式传值的优化

假设开发父组件和子组件的是两个人,那么开发父组件的人在使用子组件时。

一时是找不到子组件中发出的自定义事件在哪里的。

因为这都是写在业务逻辑里,代码量比较多的时候确实比较难找。而且这些事件也缺乏有效的管理。

因此Vue3.x提供了emits组合式API的写法去优化上面的内容。

1)数组写法对发出的事件进行注册说明

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template> <div class="add"> <button @click="btnClick(1)">+1</button> <button @click="btnClick(5)">+5</button> <button @click="btnClick(10)">+10</button> </div> </template> <script> export default { // emits数组语法(把发出的自定义事件全部写到这里面) // 相当于把发出的事件进行注册说明一下,这个写法是非必须的 emits: ["add"], methods: { btnClick(count) { this.$emit("add", count) } } } </script>
  • emits数组写法有两个好处
    • 让调用者更清晰子组件中发出了什么事件
    • 使用vscode开发的话,还可以获得事件提示,对开发更加友好,如果不写则没有事件提示

2)对象写法对发出的事件参数进行参数验证

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<template> <div class="add"> <button @click="btnClick(1)">+1</button> <button @click="btnClick(5)">+5</button> <button @click="btnClick(10)">+10</button> </div> </template> <script> export default { // emmits对象语法,对参数count进行验证 emits: { add: function(count) { if (count <= 10) { return true } return false } }, methods: { btnClick(count) { console.log("btnClick:", count) this.$emit("add", 100) } } } </script>

对象语法在写业务逻辑的时候用的可能会少一些,一般只用数组语法来进行优化就可以了。

除非是封装一些严格的组件库的时候,可以对事件参数作出相应的验证。

四、组件通信-非父子组件

4.1 Vue3中的Provide/Inject(依赖注入)方式

1)概念和使用场景

Provide/Inject用于非父子组件之间共享数据。这也是Vue3新提供的一些特性。

如果有些深度嵌套的组件,子组件想要获取父组件的部分内容。

在这种情况下,如果仍然将props沿着组件链逐级传递下去,就会非常的麻烦。

此时可以使用 ProvideInject ,无论层级结构有多深,父组件都可以作为其所有子组件的依赖 提供者。

父组件有一个 provide 选项来提供数据,子组件有一个 inject 选项来开始使用这些数据。

父组件不需要知道哪些子组件使用它使用了它提供的属性。 子组件也不需要知道 注入的属性来自哪里。

2)代码示例

A组件:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template> <div class="app"></div> </template> <script> import Home from './B.vue' export default { components: { B }, // provide一般都是写成函数 provide() { return { name: "张三", age: 25 } } } </script>

父组件提供一个对象数据。

B组件:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
<template> <div class="app"></div> </template> <script> import Home from './C.vue' export default { components: { C }, } </script>

C组件:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
<template> <div class="banner"> <h2> {{ name }} - {{ age }}</h2> </div> </template> <script> export default { inject: ["name", "age"] } </script>

C组件并不是A组件的直接子组件。但是可以使用inject的方式接收到。

今后开发Vue3项目,可能这种方式用的不多,因为可以使用Vuex或者pinia去共享数据。

4.2 Vue3中的事件总线方式

Vue3从实例中移除了 $on$off$once 方法。

也就是说我们无法像Vue2中那种方式去使用事件总线。也不能直接把公共Vue

所以我们如果希望继续使用全局事件总线,可以通过 Vue3官方有推荐一些库。

mitt或者tiny-emitter。 这里就以mitt为例。

1)npm安装

复制代码
1
2
num install mitt -s

2)新建一个event-bus.js作为事件总线

复制代码
1
2
3
4
import mitt from 'mitt' const emitter = mitt() export default emitter

3)A组件触发自定义事件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template> <div> <h1>A组件</h1> <button @click> 按钮 </button> </div> </template> <script> import emitter from '../plugins/event-bus.js' export default { created(){ emitter.emit("fn",{name:'张三',age:19}) } } </script>

4)B组件监听和移除自定义事件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<template> <div> <h1>B组件</h1> {{ str }} </div> </template> <script > import emitter from '../plugins/event-bus.js' export default { created(){ // 监听fn事件 emitter.on("fn",msg=>{ console.log(msg); }); }, methods: { eventHandler() { console.log("已经终止事件监听") } }, unmounted() { // 取消监听fn函数 eventBus.off("fn", this.eventHandler) } } </script>

按照更加严谨的做法,组件中需要监听的同时也要取消监听。

这种方式不要乱用,如果在代码中大量的使用事件总线代码,其实是不便于管理和维护的

最后

以上就是鲜艳衬衫最近收集整理的关于Vue组件化开发--组件通信方式-父传子、子传父、非父子组件传值的全部内容,更多相关Vue组件化开发--组件通信方式-父传子、子传父、非父子组件传值内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(111)

评论列表共有 0 条评论

立即
投稿
返回
顶部