vue(十一) 组件一 组件之间的通讯


组件

组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。在实际应用中,组件常常被组织成层层嵌套的树状结构:
在这里插入图片描述
和嵌套 HTML 元素的方式类似,Vue 实现了自己的组件模型,使我们可以在每个组件内封装自定义内容与逻辑。Vue 同样也能很好地配合原生 Web Component

1. 定义一个组件

Vue 的单文件组件 (即 *.vue 文件,英文 Single-File Component,简称 SFC) 是一种特殊的文件格式,使我们能够将一个 Vue 组件的模板、逻辑与样式封装在单个文件中。

<script setup>
import { ref } from 'vue'
const greeting = ref('Hello World!')
</script>

<template>
  <p class="greeting">{{ greeting }}</p>
</template>

<style>
.greeting {
  color: red;
  font-weight: bold;
}
</style>

Vue 的单文件组件是网页开发中 HTML、CSS 和 JavaScript 三种语言经典组合的自然延伸。、

import { ref } from 'vue'

export default {
  setup() {
    const count = ref(0)
    return { count }
  },
  template: `
    <button @click="count++">
      You clicked me {{ count }} times.
    </button>`
  // 也可以针对一个 DOM 内联模板:
  // template: '#my-template-element'
}

上面的例子中定义了一个组件,并在一个 .js 文件里默认导出了它自己,但你也可以通过具名导出在一个文件中导出多个组件。

2. 使用组件

要使用一个子组件,需要在父组件中导入它。

  1. 也可以全局地注册一个组件,使得它在当前应用中的任何组件上都可以使用,而不需要额外再导入。
  2. 组件可以多次使用
    <script setup>
    import ButtonCounter from './ButtonCounter.vue'
    </script>
    
    <template>
      <h1>Here is a child component!</h1>
      <ButtonCounter />
    </template>
    
    通过 <script setup>,导入的组件都在模板中直接可用。

父子传参

1.父传子(props)

  • 方式一:父组件给子组件传值时,通过v-on绑定属性实现,子组件通过props进行接收

    // 父组件
    <template>
      <div>父组件</div>
      <Childs :msg="msg"  :count="count"></Childs>
    </template>
    <script lang="ts">
    import { defineComponent, ref } from 'vue'
    import Childs from './childCom.vue'
    export default defineComponent({
      components: {
        Childs
      },
      setup () {
        const msg = ref('这是传递给子组件的')
        const count = ref(10)
        return {
          msg,
          count
        }
      }
    })
    </script>
    // 子组件
    <template>
    子组件
    <div>{{ msg }}</div>
    <div>{{ count }}</div>
    </template>
    <script lang="ts">
    import { defineComponent, ref } from 'vue'
    
    export default defineComponent({
      props: {
        msg: {
          type: String,
          default: ''
        },
        count: {
          type: Number,
          default: 0
        }
      },
      setup () {
        return {
        }
      }
    })
    </script>
    

    上述代码执行结果

  • 方式二:父组件通过插槽(slot)向子组件传递参数

    子组件中通过slot标签传递参数
    父组件中通过template插入内容,通过v-slot可以接收插槽传递的数据

    	// 父组件
    	<template>
    		  <div>父组件</div>
    		  <Childs>
    		    <template #default="{ message }">
    		      {{ message }}
    	    	</template>
    		  </Childs>
    	</template>
    	<script lang="ts">
    		import { defineComponent, ref } from 'vue'
    		import Childs from './childCom.vue'
    		export default defineComponent({
    		  components: {
    		    Childs
    		  },
    		  setup () {
    		    return {
    		    }
    		  }
    		})
    	</script>
    	// 子组件
    	<template>
    	子组件 <slot :message="message"></slot>
    	</template>
    	<script lang="ts">
    	import { defineComponent, ref } from 'vue'
    	export default defineComponent({
    	  setup (_, { emit }) {
    	    const message = ref('子组件message')
    	    return {
    	      message
    	    }
    	  }
    	})
    	</script>
    	```
    	![上述代码执行效果](https://img-blog.csdnimg.cn/direct/537301cf18ac42a3a0eb7fac10f955c6.png)
    注意:插槽只能用于传递静态内容,如果需要传递动态内容,则需要使用props或者provide/inject来实现。此外,插槽还可以定义多个,并且可以通过name属性来区分不同的插槽。(插槽后面会有具体的文章介绍)
    
  • v-model,子组件可直接修改父组件传递过来的值
    在Vue3中,可以使用v-model指令来实现子组件直接修改父组件传递过来的值。具体来说,可以在父组件中使用v-model指令将一个变量与子组件的一个prop绑定起来,然后在子组件中使用emit方法触发一个名为update:加上prop名字的事件,并传递一个新的值作为参数。

    // 父组件
    <template>
      <div>父组件</div>
      <Childs v-model:isShow="isShow"></Childs>
    </template>
    <script lang="ts">
    import { defineComponent, ref } from 'vue'
    import Childs from './childCom.vue'
    export default defineComponent({
      components: {
        Childs
      },
      setup () {
    	  const isShow = ref(true)
        return {
          isShow
        }
      }
    })
    </script>
    
    // 子组件
    <template>
    子组件
    <div>{{ count }}</div> -->
    <div>{{ isShow }}</div>
    </template>
    <script lang="ts">
    import { defineComponent, ref } from 'vue'
    
    export default defineComponent({
      props: {
        isShow: {
          type: Boolean,
          default: false
        }
      },
      setup (_, { emit }) {
        return {
        }
      }
    })
    </script>
    

2.子传父(emit)

```cpp
// 父组件
<template>
  <div>父组件</div>
  <Childs :msg="msg"  :count="count" @handleClick="handleClick"></Childs>
  <div>子组件传入父组件:{{ childMsg }}</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
import Childs from './childCom.vue'
export default defineComponent({
  components: {
    Childs
  },
  setup () {
    const msg = ref('这是传递给子组件的')
    const count = ref(10)
    const childMsg = ref<string>('')
    const handleClick = (data: string) => {
      console.log(data)
      childMsg.value = data
    }
    return {
      msg,
      count,
      handleClick,
      childMsg
    }
  }
})
</script>
// 子组件
<template>
	子组件
	<div>{{ msg }}</div>
	<div>{{ count }}</div>
	<button @click="handleClick">按钮</button>
	</template>
	<script lang="ts">
	import { defineComponent, ref } from 'vue'
	
	export default defineComponent({
	  props: {
	    msg: {
	      type: String,
	      default: ''
	    },
	    count: {
	      type: Number,
	      default: 0
	    }
	  },
	  setup (_, { emit }) {
	    const handleClick = () => {
	      emit('handleClick', '我是子组件的信息')
	    }
	    return {
	      handleClick
	    }
	  }
	})
</script>
```

兄弟之间传参

Vue 3 中移除了 eventBus,但可以借助第三方工具来完成。Vue 官方推荐使用mitt或 tiny-emitter。

  1. 安装
    yarn add mitt -S
    
  2. 注册
    创建eventBus.ts
    // eventBus.ts
    import mitt from 'mitt'
    const mitter = mitt();
    export default mitter
    
  3. 使用
    创建 eventBus.vue
    <template>
      <div>父组件</div>
      <EventBusA />
      <EventBusB />
    </template>
    <script lang="ts">
    import { defineComponent, ref } from 'vue'
    import EventBusA from './components/eventBusA.vue'
    import EventBusB from './components/eventBusB.vue'
    export default defineComponent({
      components: {
        EventBusA,
        EventBusB
      },
      setup () {  
        return {
    
        }
      }
    })
    </script>
    
    创建 eventBusA.vue
    <template>
      <div>A组件</div>
      <div>{{ message }}</div>
    </template>
    <script lang="ts">
    import { defineComponent, ref } from 'vue'
    import mitter from '../eventBus'
    export default defineComponent({
      components: {
      },
      setup () {
        const message = ref<string>('')
        mitter.on('msgB', (data) => {
        // msgB是emit传的字段, data是传的字段值
          message.value = data
        })
        return {
          message
        }
      }
    })
    </script>
    
    创建 eventBusB.vue
    <template>
      <div>B组件</div>
      <button type="button" @click="handleClick">点击传参A</button>
    </template>
    <script lang="ts">
    import { defineComponent, ref } from 'vue'
    import mitter from '../eventBus'
    export default defineComponent({
      components: {
      },
      setup () {
        const handleClick = () => {
          // emit(key, value) key: 字段, value:字段值
          mitter.emit('msgB', '我是B组件传给A组件')
        }
        return {
          handleClick
        }
      }
    })
    </script>
    

跨级组件之间的通信

provide/inject来实现跨级组件之间的通信
创建parent.vue

<template>
  <div>父组件</div>
  <brotherOne />
  <brotherTwo />
</template>
<script lang="ts">
import { defineComponent, ref, provide } from 'vue'
import brotherOne from './components/brotherOne.vue'
import brotherTwo from './components/brotherTwo.vue'
export default defineComponent({
  components: {
    brotherOne,
    brotherTwo
  },
  setup () {  
    const msg = ref('给孙组件传递的值')
    provide('msg', msg)
    
    return {
    }
  }
})
</script>

创建brotherOne.vue

<template>
  <div>老大组件: {{ message }}</div>
  <oneSon />
</template>
<script lang="ts">
import { defineComponent, inject, ref } from 'vue'
import oneSon from './oneSon.vue'
export default defineComponent({
  components: {
    oneSon
  },
  setup () {
    const message = inject('msg')
    return {
      message
    }
  }
})
</script>

创建brotherTwo.vue

<template>
  <div>老二组件: {{ message }}</div>
  <twoSon />
</template>
<script lang="ts">
import { defineComponent, inject, ref } from 'vue'
import twoSon from './twoSon.vue'
export default defineComponent({
  components: {
    twoSon
  },
  setup () {  
    const message = inject('msg')
    return {
      message
    }
  }
})
</script>

创建oneSon.vue

<template>
  <div>老大Son组件: {{ message }}</div>
</template>
<script lang="ts">
import { defineComponent, inject, ref } from 'vue'
export default defineComponent({
  setup () {  
    const message = inject('msg')
    return {
      message
    }
  }
})
</script>

创建twoSon.vue

<template>
  <div>老二Son组件: {{ message }}</div>
</template>
<script lang="ts">
import { defineComponent, inject, ref } from 'vue'
export default defineComponent({
  setup () {  
    const message = inject('msg')
    return {
      message
    }
  }
})
</script>

上述代码执行效果

非父子组件之间的通信

Vuex 和 Pinia 是 Vue 3 中的状态管理工具,使用这两个工具可以轻松实现组件通信。后面会写文章详细讲解。

子组件为何不可以修改父组件传递的 Prop

在Vue中,子组件不能直接修改父组件传递的prop,这是为了确保单向数据流的原则和组件间的数据流动清晰。

  1. 单向数据流:Vue遵循单向数据流的设计原则,即数据从父组件流向子组件。这种设计使得数据的流动方向明确,便于追踪和调试。如果允许子组件直接修改父组件传递的prop,会导致数据的变更不可预测,增加代码的复杂性和维护成本。

  2. 可维护性和可预测性:当子组件修改了父组件传递的prop时,父组件的状态可能会被不可预测地改变,导致代码难以维护和调试。通过限制子组件对prop的修改,可以确保父组件的状态不会被子组件意外地改变,提高代码的可维护性和可预测性。

如果子组件需要修改父组件的数据,可以通过触发事件(使用$emit)或者使用Vuex进行全局状态管理来实现。父组件可以监听子组件触发的事件或者从Vuex中获取数据变更,然后在相应的处理函数中进行状态的更新。这样可以保持数据流的单向性,同时实现子组件对父组件数据的影响。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值