一、props:父传子

也可用于子传父,前置条件是父先给子一个函数

1. 父组件Father.vue

<template>
    <h2>父组件</h2>

    <h4>车子:{{ car }}</h4>
    <h4 v-show="toy">子组件给的数据:{{ toy }}</h4>
    <Child :sendToy="getToy" :parents="parents"/>
</template>

<script setup lang="ts">
// 子组件
import Child from './Child.vue'
import {ref, reactive} from 'vue'

// 数据
let parents = reactive({
  name: "张三",
  age: 20
})
let toy = ref('')

// 此方法给传递给子组件调用传参给父
function getToy(value: string) {
  toy.value = value
}
</script>

2. 子组件Child.vue

<template>
    <h2>子组件</h2>

    <h2>父给的数据:{{ parents }}</h2>
    <button @click="sendToy(toy)">把 toy 给父</button>
</template>

<script setup lang="ts">
import {ref} from 'vue'
// 数据
let toy = ref('奥特曼')
// 声明接收props
defineProps(['car', 'sendToy', "parents"])
</script>

二、自定义事件:子传父

1. 父组件Father.vue

<template>
    <h2>父组件</h2>
    <h3 v-show="toy">子给的数据:{{ toy }}</h3>
    <!-- 给子组件Child绑定自定义事件 -->
    <Child @send-toy="saveToy"/>

</template>

<script setup lang="ts">
import Child from './Child.vue'
import {ref} from "vue";
// 数据
let toy = ref('')

// 用于保存传递过来的数据
function saveToy(value: string) {
  toy.value = value
}
</script>

2. 子组件Child.vue

<template>
    <h2>子组件</h2>
    <h3>玩具:{{ toy }}</h3>
    <button @click="emit('send-toy',toy)">把玩具传给父</button>
  </div>
</template>

<script setup lang="ts">
import {ref} from "vue";
// 数据
let toy = ref('奥特曼')
// 声明事件
const emit = defineEmits(['send-toy'])
</script>

三、mitt:任意组件通信

1. 安装

npm i mitt

2. 创建文件:src\utils\emitter.ts

// 引入mitt
import mitt from 'mitt'

// 调用mitt得到emitter,emitter能:绑定事件、触发事件
const emitter = mitt()

export default emitter

3. Child1.vue传递数据

<template>
  <div class="child1">
    <h2>子组件1</h2>
    <h3>玩具:{{ toy }}</h3>
    
    // 触发send-toy事件,并传递数据toy
    <button @click="emitter.emit('send-toy',toy)">传递toy给监听send-toy事件的组件</button>
  </div>
</template>

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

let toy = ref('奥特曼')
</script>

4. Child2.vue接收数据

<template>
  <div class="child2">
    <h2>子组件2</h2>
    <h3>电脑:{{ computer }}</h3>
    <h3>Child1.vue给的数据:{{ toy }}</h3>
  </div>
</template>

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

let computer = ref('联想')
let toy = ref('')

// 给emitter绑定send-toy事件
emitter.on('send-toy', (value: any) => {
  toy.value = value
})

// 在组件卸载时解绑send-toy事件
onUnmounted(() => {
  emitter.off('send-toy')
})
</script>

四、v-model:父子互传,用的较少

1. 先理解v-model底层如何实现

<template>

<input type="text" v-model="username"> 
等价于:
<input type="text" :value="username" @input="username = (<HTMLInputElement>$event.target).value"> 
// <HTMLInputElement>是为了ts类型检查需加上,否则会报红
</template>

2. 假如我要封装一个自己的<el-input>要怎么实现?

相信大家都用过 <el-input v-model="username">

上面第1点中<input type="text" v-model="username">就是给input绑定一个自定义事件实现双向绑定,其实组件也是如此。


<el-input v-model="username"/>
等价于:
<el-input :modelValue="username" @update:modelValue="username = $event"/>

update:modelValue其实就是一个自定义事件名,这是vue规定的

$event就是触发自定义事件传递过来的数据

这也就意味着我封装自己的<el-input>也是如此

<MyInput v-model="username"/>
等价于:
<MyInput :modelValue="username" @update:modelValue="username = $event"/>

3. 那么MyInput组件内部要怎么写呢?

<template>
  // 2. 像第一点那样展示数据,绑定自定义事件即可
  <input 
    type="text" 
    :value="modelValue"
    @input="emit('update:modelValue',(<HTMLInputElement>$event.target).value)"
  >

</template>

<script setup lang="ts" >
  // 1. 先接收数据与自定义事件
  defineProps(['modelValue'])
  const emit = defineEmits(['update:modelValue'])
</script>

4. <MyInput v-model="username"/> 怎么传递两个参数?

<MyInput v-model:ming="username" v-model:mima="password"/>

等价于:

<MyInput

        :ming="username"

        @update:ming="username = $event"

        :mima="password"

        @update:mima="password= $event"

 />

其实就是改了modelValue

5. 接收时不变

<template>
  <input 
    type="text" 
    :value="ming"
    @input="emit('update:ming',(<HTMLInputElement>$event.target).value)"
  >
  <br>
  <input 
    type="text" 
    :value="mima"
    @input="emit('update:mima',(<HTMLInputElement>$event.target).value)"
  >
</template>

<script setup lang="ts" >
  defineProps(['ming','mima'])
  const emit = defineEmits(['update:ming','update:mima'])
</script>

五、$attrs:爷孙互传

1. 父组件:Father.vue

<template>
    <h4>a:{{ a }}</h4>
    <Child :a="a" v-bind="{x:100,y:200}" :updateA="updateA"/>
    // v-bind="{x:100,y:200}" 等于 :x="100" :y="200"
</template>

<script setup lang="ts">
import Child from './Child.vue'
import {ref} from 'vue'

let a = ref(1)

function updateA(value: number) {
  a.value += value
}
</script>

2. 子组件Child.vue

父给子传了数据但子没接收,数据会保存到 $attrs 中

<template>
   <GrandChild v-bind="$attrs"/>
</template>

<script setup lang="ts">
import GrandChild from './GrandChild.vue'
</script>

3. 孙组件GrandChild.vue

<template>
    <h4>a:{{ a }}</h4>
    <h4>x:{{ x }}</h4>
    <h4>y:{{ y }}</h4>
    <button @click="updateA(6)">给爷爷传递数据</button>
</template>

<script setup lang="ts">
defineProps(['a','x', 'y', 'updateA'])
</script>

六、$refs与$parent

$refs:父传子,$refs获取所有子组件的实例对象

$parent:子传父,$parent 获取父组件的实例

前提是需要用defineExpose({...数据})将数据提供给外部

 1. 父组件Father.vue

<template>
    <h3>房产:{{ house }}</h3>
    <button @click="changeToy($refs)">修改Child1的数据</button>
    <Child1 ref="c1"/>
</template>

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

let house = ref(4)

// $refs获取所有子组件的实例对象
function changeToy(refs: { [key: string]: any }) {
  refs.c1.book += 1  
}

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

2. 子组件Child1.vue

<template>
    <h3>书籍:{{ book }} 本</h3>
    <button @click="minusHouse($parent)">修改父组件的数据</button>
</template>

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

let book = ref(3)

// $parent 获取父组件的实例
function minusHouse(parent: any) {
  parent.house -= 1
}

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

七、provide、inject:祖给后代们

1. 父组件Father.vue

<script setup lang="ts">
import {ref, reactive, provide} from 'vue'

let money = ref(100)
let car = reactive({
  brand: '宝马',
  price: 10
})
function updateMoney(value: number) {
  money.value -= value
}
// 向后代提供数据
provide('moneyContext', {money, updateMoney})
provide('car', car)
</script>

2. 后代组件GrandChild.vue

<script setup lang="ts">
import {inject} from "vue";

第一个参数与provide的第一个参数一致
第二个可选,设置默认值,也可以是一个工厂函数,用来返回某些创建起来比较复杂的值。
第三个可选,类型为布尔值。当第二个参数的值就是一个函数,而不是工厂函数时,需要使用将值设置为 false。
let {money, updateMoney} = inject('moneyContext', {
  money: 0, updateMoney: (param: number) => {
  }
})
let car = inject('car', {brand: '未知', price: 0})
</script>

八、pinia

可查看之前写的文章:pinia的基本使用,从入门到精通-CSDN博客

九、slot(插槽)

1. 默认插槽

<Category>
    <h1>张三<h1>
</Category>

Category组件中:
<template>
    <slot>默认内容</slot>
</template>

2. 具名插槽

<Category>
    <template #s2>
         <h2>李四</h2>
    </template>
    <template #s1>
          <h2>张三</h2>
    </template>
</Category>

Category组件中:
<template>
    <slot name="s1">默认内容1</slot>
    <slot name="s2">默认内容2</slot>
</template>

3. 作用域插槽

<Game>
  // qwe是组件中slot的name属性
  // params是组件传递过来的数据,是一个对象
  <template v-slot:qwe="params">
     <ul>
        <li v-for="y in params.games" :key="y.id">
           {{ y.name }}
        </li>
     </ul>
  </template>
  // #defaule是将内容放入默认插槽
  <template #defaule="params">
     <h2>{{ params.title }}</h2>
  </template>
</Game>


Game组件中:
<template>
    <slot :title="title"></slot>
    <slot name="qwe" :games="games"></slot>
</template>
<script setup lang="ts">
  import {reactive,ref} from 'vue'
  let title= ref("游戏列表")
  let games = reactive([
    {id:'1',name:'lol'},
    {id:'2',name:'王者'},
    {id:'3',name:'吃鸡'}
  ])
</script>

Logo

前往低代码交流专区

更多推荐