Vue3 组件通信_vue

一、props

概述:props是使用频率最高的一种通信方式,常用与 :父 ↔ 子

  • 父传子:属性值是非函数
  • 子传父:属性值是函数
1.1、父组件
<template>
  <div class="app">
    <h2>父组件</h2>
    <p v-if="drug">
      子组件中的药剂:{{ drug }}
    </p>
    <p>
      余额:{{ amount }}
    </p>

    <hr />
    <Subcomponent :money="amount" :sendDrug="getDrug" />
  </div>
</template>


<script setup lang="ts" name="App">
  import Subcomponent from './components/Subcomponent.vue';
  import { ref } from 'vue';

  // 数据
  let amount = ref(666)
  let drug = ref('')

  // 方法
  function getDrug(d: string) {
    drug.value = d
  }

</script>


<style lang="scss" scoped>
  .app {
    margin: 150px;
    padding: 30px;
    background-color: bisque;
  }
</style>
  • 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.
1.2、子组件
<template>
  <div class="subcomponent">
    <h2>子组件</h2>
    
    <p>
      父组件中的余额:{{ money }}
    </p>
    <p>
      药剂:{{ drug }}
    </p>

    <button @click="sendDrug(drug)">传你一副药剂</button>
  </div>
</template>


<script setup lang="ts" name="Subcomponent">
  import { ref } from 'vue';

  // 接收父组件传来的参数
  defineProps(['money', 'sendDrug'])
  // 或者下面这样接收。下面这种接收方式就可以在当前setup中直接调用sendDrug('xxx')
  // const props = defineProps<{money?: number, sendDrug: Function}>()
  // let { money, sendDrug } = props
  // sendDrug('xxx')

  let drug = ref('春嘿嘿')
</script>


<style lang="scss" scoped>
  .subcomponent {
    margin: 50px;
    padding: 50px;
    background-color: coral;
  }
  button {
    margin-right: 20px;
    background-color: beige;
  }
</style>
  • 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.
1.3、最终效果(父组件向子组件传余额,以及修改药剂的函数)

Vue3 组件通信_vue组件通信_02

二、Pinia

2.1、在src/store目录下新建一个xxx.ts文件。(这就相当于共享数据)
import axios from "axios";
import { defineStore } from "pinia";
import { reactive } from "vue";

const useLoveTalkStore = defineStore('love-talk-id', () => {
    // 相当于[state-状态]    先从浏览器的 localStorage 中取值
    const loveTalks: string[] = reactive(JSON.parse(localStorage.getItem('loveTalks') as string) || [])

    // 相当于[actions-动作]
    function addLoveTalk() {
        // loveTalks 数组头部插入
        loveTalks.unshift(content)
        // 存储到浏览器的 localStorage 中,实现页面刷新数据不丢失
        localStorage.setItem('loveTalks', JSON.stringify(loveTalks))
    }
      
    // 向外暴露
    return { loveTalks }
})

// 暴露useLoveTalkStore
export default useLoveTalkStore
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
2.2、在其他组件中引入xxx.ts文件,然后就都能获取到state数据,同时也可根据xxx.ts文件中提供的方法对state进行修改,这样就相当于各组件实现相互通信。
// 引入loveTalk.ts
import useLoveTalkStore from './store/loveTalk';
import { storeToRefs } from 'pinia';
import { toRefs } from 'vue';

let loveTalkStore = useLoveTalkStore()
// 可以直接从 useLoveTalkStore() 结果中解构方法
const { addLoveTalk } = loveTalkStore
const { loveTalks } = storeToRefs(loveTalkStore)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

三、mitt(其实就是先在一个组件中绑定事件,然后其他组件只需要触发这个事件就能完成组件之间的通信。只不过你想做什么样的操作,就需要先绑定什么样的事件)

3.1、安装mitt
npm i mitt
  • 1.
3.2、新建src/utils/emitter.ts文件,创建mitt实例并暴露
// 引入mitt
import mitt from "mitt";

// 创建mitt
const emitter = mitt()

// 暴露mitt
export default emitter
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
3.3、在接收数据方绑定事件。(这里选择子组件Subcomponent.vue作为接收数据方)
<template>
  <div class="subcomponent">
    <h2>子组件</h2>
    <p>
      电脑:{{ computer }}
    </p>
  </div>
</template>


<script setup lang="ts" name="Subcomponent">
  import emitter from '@/utils/emitter';
  import { onUnmounted, ref } from 'vue';

  // 数据
  let computer = ref<string>('惠普-暗影精灵')

  // 组件创建时,绑定事件[modify-computer]
  emitter.on('modify-computer', (value: any) => {
    computer.value = value
  })

  // 组件卸载时,解绑事件!!!!!!
  onUnmounted(() => {
    emitter.off('modify-computer')
  })
</script>


<style lang="scss" scoped>
  .subcomponent {
    margin: 50px;
    padding: 50px;
    background-color: coral;
  }

  button {
    margin-right: 20px;
    background-color: beige;
  }
</style>
  • 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.
3.4、在提供数据方触发事件。(这里选择父组件App.vue作为提供数据方)
<template>
  <div class="app">
    <h2>父组件</h2>
    <button @click="changeSubComputer">改变子组件中的电脑</button>
    <hr />
    <Subcomponent />
  </div>
</template>


<script setup lang="ts" name="App">
  import Subcomponent from './components/Subcomponent.vue';
  import emitter from './utils/emitter';

  // 方法。触发子组件中的事件
  function changeSubComputer() {
    emitter.emit('modify-computer', '联想')
  }
</script>


<style lang="scss" scoped>
  .app {
    margin: 150px;
    padding: 30px;
    background-color: bisque;
  }
</style>
  • 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.
3.5、效果图

Vue3 组件通信_vue_03

四、slot【作用域插槽】

数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。

4.1、定义一个共用组件Subcomponent.vue。(数据和插槽都在当前组件中)
<template>
  <div class="subcomponent">
    <!-- <h2>子组件</h2> -->
    <!-- 给插槽绑定数据,谁使用了插槽就能拿到这些数据 -->
    <slot name="s1" :persons="persons" :ss="123">未知异常</slot>
  </div>
</template>


<script setup lang="ts" name="Subcomponent">
  import { reactive } from 'vue';

  // 数据和插槽在同一个组件中。(可重复使用当前组件来实现同一数据不同样式)
  let persons = reactive([
    { id: '001', name: '张三' },
    { id: '002', name: '李四' },
    { id: '003', name: '王五' },
    { id: '004', name: '赵六' }
  ])
</script>


<style lang="scss" scoped>
  .subcomponent {
    background-color: skyblue;
    border-radius: 10px;
    box-shadow: 0 0 10px;
    padding: 10px;
    width: 200px;
    height: 300px;
  }

  button {
    margin-right: 20px;
    background-color: beige;
  }
</style>
  • 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.
4.2、定义使用公用组件的组件App.vue。(主要实现页面结构样式)

使用插槽时,可以在template中使用#s1="params"来获取插槽中绑定的数据,并通过params.persons、params.ss的方式取出绑定的多个值。

<template>
  <div class="app">
    <!-- <h2>父组件</h2> -->
    <!-- <hr /> -->
     <!-- #s1 相当于 v-slot:s1 -->
    <div class="content">
      <Subcomponent>
      <!-- #s1="params":取出插槽中绑定的所有数据。也可以不叫params,取名随意。 -->
      <template #s1="params">
        <h2>有序列表</h2>
        <ul>
          <li v-for="(p, index) in params.persons" :key="p.id">
            {{ p.name }}
          </li>
        </ul>

        <p>
          <span>展示测试的值:{{ params.ss }}</span>
        </p>
      </template>
    </Subcomponent>

    <Subcomponent>
      <template v-slot:s1="params">
        <h2>有序列表</h2>
        <ol>
          <li v-for="(p, index) in params.persons" :key="p.id">
            {{ p.name }}
          </li>
        </ol>
      </template>
    </Subcomponent>

    <Subcomponent>
      <template #s1="params">
        <h2>人物列表</h2>
        <p v-for="(p, index) in params.persons" :key="p.id">
          {{ p.name }}
        </p>
        <!-- <h2>短视频</h2> -->
        <!-- <video :src="videoUrl" controls /> -->
      </template>
    </Subcomponent>
    </div>
  </div>
</template>


<script setup lang="ts" name="App">
  import Subcomponent from "./components/Subcomponent.vue";
  import { ref, reactive } from "vue";

  // 当前页面不需要定义数据,数据都是从插槽中获取。
  // let persons = reactive([
  //   { id: '001', name: '张三' },
  //   { id: '002', name: '李四' },
  //   { id: '003', name: '王五' },
  //   { id: '004', name: '赵六' }
  // ])
  // let imgUrl = ref('https://img1.baidu.com/it/u=2723523495,2549185469&fm=253&fmt=auto&app=120&f=JPEG?w=1280&h=800"}')
  // let videoUrl = ref('https://media.w3.org/2010/05/sintel/trailer.mp4')
</script>


<style lang="scss" scoped>
  .app {
    background-color: pink;
    border-radius: 10px;
    width: 800px;
    margin: auto;
    margin-top: 100px;
    padding: 20px;
  }

  .content {
    display: flex;
    justify-content: space-evenly;
  }

  img,video {
    width: 100%;
  }

  h2 {
    background-color: orange;
    text-align: center;
    font-size: 20px;
    font-weight: 800;
  }
</style>
  • 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.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
4.3、效果图。(相同的数据但以不同的样式展示。)

Vue3 组件通信_vue组件通信_04

五、自定义事件

5.1、在父组件App.vue中对引入的子组件Subcomponent.vue添加自定义事件<Subcomponent @modify-car="car = $event" />
<template>
  <div class="app">
    <h2>父组件</h2>
    <p>
      车:{{ car }}
    </p>
    <hr />
    <!-- 添加自定义事件 -->
    <Subcomponent @modify-car="car = $event" />
  </div>
</template>


<script setup lang="ts" name="App">
  import { ref } from 'vue';
  import Subcomponent from './components/Subcomponent.vue';

  let car = ref('法拉利')
</script>


<style lang="scss" scoped>
  .app {
    margin: 150px;
    padding: 30px;
    background-color: bisque;
  }
</style>
  • 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.
5.2、在子组件Subcomponent.vue中声明可以触发的自定义事件,以及触发事件
<template>
  <div class="subcomponent">
    <h2>子组件</h2>
    <button @click="modifyCar">修改父组件中的车</button>
  </div>
</template>


<script setup lang="ts" name="Subcomponent">
  // 声明组件可以触发的自定义事件
  const emit = defineEmits(['modify-car'])

  // 方法
  function modifyCar() {
    // 触发自定义事件
    emit('modify-car', '五菱宏光')
  }
</script>


<style lang="scss" scoped>
  .subcomponent {
    margin: 50px;
    padding: 50px;
    background-color: coral;
  }

  button {
    margin-right: 20px;
    background-color: beige;
  }
</style>
  • 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.
5.3、效果图。(点击子组件中的按钮可对父组件中的车数据进行修改)

Vue3 组件通信_vue_05

六、$attrs

$attrs用于实现当前组件的父组件,向当前组件的子组件通信(祖→孙)。

$attrs是一个对象,包含所有父组件传入的标签属性。

注意:$attrs会自动排除props中声明的属性(可以认为声明过的 props 被子组件自己“消费”了)

6.1、父组件Parentcomponent.vue。(在引入的子组件标签中传递属性值)
<template>
  <div class="parentcomponent">
    <h2>父组件: [a = {{ a }}]       [b = {{ b }}]       [c = {{ c }}]
    </h2>
    <hr />
    <!-- 传入a, b, c, modifyName 等标签属性 -->
    <Subcomponent :a="a" :b="b" :c="c" :modifyName="modifyName" />
  </div>
</template>


<script setup lang="ts" name="Parentcomponent">
  import { reactive, ref } from 'vue';
  import Subcomponent from './Subcomponent.vue';

  // 数据
  let a = ref(1)
  let b = ref('qq')
  let c = reactive({ id: '001', name: '张三' })

  // 方法
  function modifyName(value: string): void {
    Object.assign(c, { name: value })
  }
</script>


<style lang="scss" scoped>
  .parentcomponent {
    margin: auto;
    margin-top: 50px;
    padding: 30px;
    background-color: antiquewhite;
    width: 900px;
  }
</style>
  • 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.
6.2、子组件(在引入的孙组件标签中添加v-bind="$attrs"即可传递属性)
<template>
  <div class="subcomponent">
    <h2>子组件</h2>
    <hr />
    <!-- 需要使用 v-bind="$attrs" 。 否则孙组件中不会接收到父组件中传来的标签属性 -->
    <Grandsoncomponent v-bind="$attrs" />
  </div>
</template>


<script setup lang="ts" name="Subcomponent">
  import Grandsoncomponent from './Grandsoncomponent.vue';

  // $attrs会自动排除以下props中声明的属性
  // 如下定义,则孙组件无法获取父组件传来的属性a的值
  defineProps(['a'])
</script>


<style lang="scss" scoped>
  .subcomponent {
    margin: auto;
    margin-top: 30px;
    padding: 30px;
    background-color: bisque;
  }
</style>
  • 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.
6.3、孙组件(通过props声明并接收父类组件传来的属性)
<template>
  <div class="grandsoncomponent">
    <h2>孙组件</h2>
    <p>
      接收到'父组件'中的值: [a = {{ a }}]       [b = {{ b }}]       [c = {{ c
      }}]
    </p>

    <button @click="changeNameForParentcomponent">更新'父组件'中的名字</button>
  </div>
</template>


<script setup lang="ts" name="Grandsoncomponent">
  import { toRefs } from 'vue';

  // 声明并接收'父组件'中传来props属性
  const props = defineProps<{ a?: number, b?: string, c?: object, modifyName?: Function }>()
  console.log('props === ', props);
  const { a, b, c } = toRefs(props)

  function changeNameForParentcomponent() {
    // ?. modifyName 是函数就调用它;否则不执行任何操作
    props.modifyName?.('李四~~~')
  }
</script>


<style lang="scss" scoped>
  .grandsoncomponent {
    margin: auto;
    margin-top: 30px;
    padding: 30px;
    background-color: beige;
  }
</style>
  • 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.
6.4、效果图。(通过孙组件中的按钮可修改父组件中的数据,实现祖孙通信)

Vue3 组件通信_vue组件通信_06

七、$refs、$parent

$refs:值为对象,包含所有被ref属性标识的DOM元素或组件实例。常用于 :父→子

$parent:值为对象,当前组件的父组件实例对象。常用于:子→父

7.1、父组件Father.vue(通过$refs拿到当前组件的所有子组件对象,并且可对子组件暴露的数据进行修改)
<template>
	<div class="father">
		<h3>父组件</h3>
		<h4>房产:{{ house }}</h4>
		<button @click="changeToy">修改Child1的玩具</button>
		<button @click="changeComputer">修改Child2的电脑</button>
		<!-- $refs 能拿到当前组件的所有子组件。若要拿到子组件中的数据或方法则需要子组件使用defineExpose暴露 -->
		<button @click="getAllChild($refs)">让所有孩子的书变多</button>
		<!-- ref 需要绑定一个变量,例如 c1 是需要自己定义的 -->
		<Child1 ref="c1" />
		<Child2 ref="c2" />
	</div>
</template>

<script setup lang="ts" name="Father">
	import Child1 from './Child1.vue'
	import Child2 from './Child2.vue'
	import { ref } from "vue";

	// 数据
	let house = ref(4)
	let c1 = ref()
	let c2 = ref()

	// 方法
	function changeToy() {
		c1.value.toy = '小猪佩奇'
	}
	function changeComputer() {
		c2.value.computer = '华为'
	}
	// $refs 能拿到当前组件的所有子组件。若要拿到子组件中的数据或方法则需要子组件使用defineExpose暴露
	function getAllChild(refs: { [key: string]: any }) {
		console.log('$refs === ', refs)
		for (let key in refs) {
			refs[key].book += 3
		}
	}

	// 向外部提供数据
	defineExpose({ house })
</script>

<style lang="scss" scoped>
	.father {
		background-color: pink;
		padding: 20px;
		border-radius: 10px;
	}

	.father button {
		margin-bottom: 10px;
		margin-left: 10px;
	}

	button {
		background-color: bisque;
	}
</style>
  • 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.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
7.2、子组件①Child1.vue(可通过$parent拿到当前组件的父组件对象,并且可对父组件暴露的数据进行修改)
<template>
	<div class="child1">
		<h3>子组件1</h3>
		<h4>玩具:{{ toy }}</h4>
		<h4>书籍:{{ book }}</h4>
		<!-- $parent 可以拿到当前组件的父组件。若要拿到父组件中的数据或方法则需要父组件使用defineExpose暴露 -->
		<button @click="minusHouse($parent)">干掉父亲的一套房产</button>
	</div>
</template>

<script setup lang="ts" name="Child1">
	import { ref } from "vue";
	// 数据
	let toy = ref('奥特曼')
	let book = ref(3)

	// 方法
	function minusHouse(parent: any) {
		console.log('$parent === ', parent)
		parent.house -= 1
	}

	// 把数据交给外部
	defineExpose({ toy, book })

</script>

<style lang="scss" scoped>
	.child1 {
		margin-top: 20px;
		background-color: skyblue;
		padding: 20px;
		border-radius: 10px;
		box-shadow: 0 0 10px black;
	}
</style>
  • 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.
7.3、子组件②Child2.vue
<template>
	<div class="child2">
		<h3>子组件2</h3>
		<h4>电脑:{{ computer }}</h4>
		<h4>书籍:{{ book }}</h4>
	</div>
</template>


<script setup lang="ts" name="Child2">
	import { ref } from "vue";
	
	// 数据
	let computer = ref('联想')
	let book = ref(6)

	// 把数据交给外部
	defineExpose({ computer, book })
</script>


<style lang="scss" scoped>
	.child2 {
		margin-top: 20px;
		background-color: orange;
		padding: 20px;
		border-radius: 10px;
		box-shadow: 0 0 10px black;
	}
</style>
  • 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.
7.4、效果图。(通过父组件和子组件中各自的按钮实现父子组件互相通信)

Vue3 组件通信_vue_07

Vue3 组件通信_vue组件通信_08

八、provide、inject

8.1、父组件Parentcomponent.vue。(通过provide向后代组件提供数据
<template>
  <div class="parentcomponent">
    <h2>父组件</h2>
    <h4>资产:{{ money }}</h4>
    <h4>汽车:{{ car }}</h4>
    <button @click="money += 1">资产+1</button>
    <button @click="car.price += 1">汽车价格+1</button>
    <hr />
    <Subcomponent />
  </div>
</template>


<script setup lang="ts" name="Parentcomponent">
  import Subcomponent from "./Subcomponent.vue";
  import { ref, reactive, provide } from "vue";

  // 数据
  let money = ref(100)
  let car = reactive({
    brand: '奔驰',
    price: 100
  })

  // 用于更新money的方法
  function updateMoney(value: number) {
    money.value += value
  }

  // 提供数据
  provide('moneyContext', { money, updateMoney })
  provide('car', car)
</script>


<style lang="scss" scoped>
  .parentcomponent {
    margin: auto;
    margin-top: 50px;
    padding: 30px;
    background-color: antiquewhite;
    width: 900px;
  }
  button {
    margin-right: 10px;
    background-color: bisque;
  }
</style>
  • 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.
8.2、子组件Subcomponent.vue。(不需要编写任何过渡代码,不受影响)
<template>
  <div class="subcomponent">
    <h2>子组件</h2>
    <hr />
    <Grandsoncomponent />
  </div>
</template>


<script setup lang="ts" name="Subcomponent">
  import Grandsoncomponent from './Grandsoncomponent.vue';
</script>


<style lang="scss" scoped>
  .subcomponent {
    margin: auto;
    margin-top: 30px;
    padding: 30px;
    background-color: bisque;
  }
</style>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
8.3、孙组件Grandsoncomponent.vue。(通过inject来声明接收数据,并且若接收的数据不存在,可以给定其默认值
<template>
  <div class="grandsoncomponent">
    <h3>孙组件</h3>
    <h4>资产:{{ money }}</h4>
    <h4>汽车:{{ car }}</h4>
    <!-- '父组件'中的方法也可通过provide提供 -->
    <button @click="updateMoney(6)">点我资产增加</button>
  </div>
</template>


<script setup lang="ts" name="Grandsoncomponent">
  import { inject } from 'vue';
  // 注入数据
  let { money, updateMoney } = inject('moneyContext', { money: 0, updateMoney: (x: number) => {} })
  let car = inject('car')
</script>


<style lang="scss" scoped>
  .grandsoncomponent {
    margin: auto;
    margin-top: 30px;
    padding: 30px;
    background-color: beige;
  }
</style>
  • 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.
8.4、效果图。(无需通过'子组件'进行数据过渡,直接实现祖孙通信)

Vue3 组件通信_vue组件通信_09

九、v-model

9.1、父组件App.vue。(v-model的本质::moldeValueupdate:modelValue事件
<template>
  <div class="app">
    <!-- 下面两个标签是等价的 -->
    <MyInput v-model:qqq="username" />
    <!-- v-model的本质是下面这行代码 -->
    <!-- <MyInput :qqq="username" @update:qqq="username = $event" /> -->
  </div>
</template>


<script setup lang="ts" name="App">
  import MyInput from './components/MyInput.vue';
  import { ref } from 'vue';

  // 数据
  let username = ref('zhangsan')
</script>


<style lang="scss" scoped>
  .app {
    width: 700px;
    margin: auto;
  }
</style>
  • 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.
9.2、自定义输入框组件MyInput.vue。(声明 组件接收的props 和 可以触发的自定义事件)
<template>
  <div class="myInput">
    输入框:
    <!-- 将接收的qqq值赋给input元素的value属性,目的是:为了呈现数据 -->
    <!-- 给input元素绑定原生input事件,触发input事件时,进而触发update:qqq事件 -->
    <!-- v-model的本质是下面这行代码 -->
    <input
        type="text"
        :value="qqq"
        @input="allEmits('update:qqq', (<HTMLInputElement>$event.target).value)"
    />
  </div>
</template>


<script setup lang="ts" name="MyInput">
    // 声明组件接收的 props
    defineProps(['qqq'])
    // 声明组件可以触发的自定义事件
    const allEmits = defineEmits(['update:qqq'])
</script>


<style lang="scss" scoped>
    input {
        border: 2px solid rgb(0, 0, 0);
        background-image: linear-gradient(45deg, rgb(248, 194, 194), rgba(189, 242, 248, 0.552), rgb(249, 208, 218));
        height: 30px;
        font-size: 20px;
        color: rgb(255, 5, 5);
    }
</style>
  • 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.
9.3、效果图。(在引入的自定义输入框组件中修改值,当前组件的值也同步变化)

Vue3 组件通信_vue组件通信_10