Vue3魔法手册 作者 张天禹 014_组件通信

052_组件通信-方式1-props

组件关系 传递方式
父传子 1, props
2, v-model
3, $refs
4, 默认插槽, 具名插槽
子传父 1,props
2, 自定义事件
3,v-model
4, $parent
5, 作用域插槽
祖传孙,孙传祖 1,$attrs
2, provide,inject
兄弟间, 任意组件间 1, mitt
2, pinia

笔记

6.1 [props]

概述: props 是使用频率最高的一种通信方式,常用与: 父<-->子。
1, 若父传子: 属性值是非函数。
2, 若子传父: 属性值是函数, 你先给我函数,我去调用

笔记

A组件将订单传给B组件,此时你得用组件通信,如果你组件通信玩得不溜,你会非常的痛苦,这两个组件可能是父子关系,兄弟关系,祖孙关系,还是其他关系,所以如果你的组件通信不熟悉,写功能的时候你会非常痛苦,知道这样做,但是代码写不出来,所以从今天开始,我们将vue3里面常见的组件通信都给大家窜一窜,接下来做准备工作

此时我们要将A组件订单传给B组件,我们就得用组件通信,就是要实现组件之间互相通信,你给我什么,我返回给你是啥

接下来我们做准备工作,将public,src,index.html全部删除

红色部分是标题 蓝色部分是导航区

粉色部分为展示区

比如我们想学习props,我们点击props,是专门学习父子组件,如下图所示

自定义组件

mitt

v-model

$attrs

refs,parent

previde,inject

pinia

slot

准备了一个第三方 bootstrap.css样式库

将 bootstrap.css 引入 index.html

主要是src里面,给大家搭建了一个简单的路由小环境

pages 里面你能看到的是如下

router

src/main.ts

App.vue

props既可以实现父传子,也可以实现子传父

src/pages/01_props/Child.vue

复制代码
<template>
    <div class="child">
        <h3>子组件</h3>
    </div>
</template>

<script setup lang="ts" name="Props">

</script>

<style scoped>
	.child {
		background-color: skyblue;
		padding: 10px;
		box-shadow: 0 0 10px black;
		border-radius: 10px;
	}
</style>

src/pages/01_props/Father.vue

复制代码
<template>
    <div class="father">
        <h3>父组件</h3>
        <Child />
    </div>
</template>

<script setup lang="ts" name="Props">
    import Child from '@/pages/01_props/Child.vue'
</script>

<style scoped>
	.father {
		back
	}
</style>

父给子能传递数字,父给子也能传递函数,还能传递数组,也能传递对象

1,父传子

父组件传递数据

子组件接收数据

效果

2, 子传父

孩子把玩具给父亲玩一会儿

接收数据发父亲需要提前定义好一个方法

收到 'sendToy' 在模板里面就可以直接使用,在使用时牢记要把toy传过去

父直接传给子,要实现子传父,先父先传一个函数给子,子在以传参的形式传给父

使用props要实现子传父,父必须先给子传函数

父如何将收到的玩具呈现到页面上 代码-效果

使用 v-show或者v-if 实现条件渲染 代码-效果

当点击'把玩具给父亲'的按钮时,页面如下

绿色框里面属于非函数

这种不要用props传递数据,一层一层太麻烦

虽然可以实现父传子,子再传孙

虽然使用props能实现,不过太闹腾了

bootstrap 官网
复制代码
https://v5.bootcss.com/docs/getting-started/introduction/

_组件通信-方式1-props 实现代码如下:

复制代码
1, src/pages/01_props/Child.vue
<template>
    <div class="child">
        <h3>子组件</h3>
		<h4>玩具: {{ toy }}</h4>
		<h4>父给的汽车: {{ car }}</h4>
		<button @click="sendToy(toy)">把玩具给父亲</button>
    </div>
</template>

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

	// 数据 
	let toy = ref('奥特曼')

	// 声明接收props
	let props = defineProps(['car','sendToy'])

	
</script>

<style scoped>
    .child {
		background-color: skyblue;
		padding: 10px;
		box-shadow: 0 0 10px black;
		border-radius: 10px;
	}
</style>





2, src/pages/01_props/Father.vue
<template>
    <div class="father">
        <h3>父组件</h3>
        <h4>汽车: {{ car }}</h4>
        <h4 v-show="toy">子给的玩具: {{ toy }}</h4>
        <Child :car="car" :sendToy="getToy" />
    </div>
</template>


<script setup lang="ts" name="Props">
    import Child from '@/pages/01_props/Child.vue'
    import { ref } from 'vue'

    // 数据 父组件向子组件传递数据
    let car = ref('奔驰')
    let toy = ref('')

    // 方法
    function getToy(value: string) {
        // 修改数据
        toy.value = value
        // console.log('父',value)
    }
</script>

<style scoped>
    .father {
        background-color: rgb(165,164,164);
        padding: 20px;
        border-radius: 10px;    
    }
</style>

053_组件通信-方式2-自定义事件

src/pages/02_custom_event/Father.vue

复制代码
<template>
    <div class="father">
        <h3>父组件</h3>
        <Child />
    </div>
</template>

<script setup lang="ts" name="Father.vue">
import Child from '@/pages/02_custom-event/Child.vue';
</script>

<style scoped>
    .father {
        background-color: rgb(165,164,164);
        padding: 20px;
        border-radius: 10px;
    }
    .father button {
        margin-right: 5px;
    }
</style>

src/pages/02_custom_event/Child.vue

复制代码
<template>
    <div class="child">
        <h3>子组件</h3>
    </div>
</template>

<script setup lang="ts" name="Child">

</script>

<style scoped>
    .child {
        background-color: rgb(76,200,76);
        padding: 20px;
        box-shadow: 0 0 10px black;
        border-radius: 10px;
    }
</style>

指针事件:DOM操作的基本功

$event 占位符,事件对象,通过事件对象,可以获取屏幕的宽度,鼠标的位置,时间戳等指针事件参数,实现代码如下,指针事件如上图所示

复制代码
<template>
    <div class="father">
        <h3>父组件</h3>
        <button @click="test(6,7,$event)">点我</button>
        <Child />
    </div>
</template>

<script setup lang="ts" name="Father.vue">
import Child from '@/pages/02_custom-event/Child.vue';

function test(a:number, b:number,c:Event) {
    console.log('父组件被点击了', a, b,c);
}
</script>
代码-效果

点击按钮'点我',屏幕上str的值发生了改变

ref定义的响应式数据,在模板里面使用可以不写 .value

$event是事件对象,而str是字符串

也可以实现点击,返回如下内容

以上就是event事件对象的简单介绍 下面正式使用event

接下来要实现的效果是孩子要把自己的玩具交给父亲

自定义事件是典型的子传父

第四行: 当你对button进行点击事件时,qwe是不是就得调用。

第五行: abc就是事件名,这是自定义事件,xyz就是事件的回调

绿色的第四行表示: 你给组件Child绑定了一个haha事件

当触发haha事件,xyz就会调用,这就是自定义事件

接下来是你如何去触发haha事件

如果写的是 @click ,我们只需要点击就可以触发事件

如果是@keyup 按键弹起,就可以触发键盘事件

如果现在是haha,该如何处理?

第四行是给Child帮了一个事件

Child组件你需要去声明

不是你在父组件帮了一个事件,你就万事大吉,你得在子组件声明

规范是emit可以在模板中使用

你只要写 emit('haha') 就可以触发自定义事件

粉色框里面的代码,你可以在任何你喜欢的地方写上他就会触发这个自定义事件

等组件挂载结束3秒以后在触发haha事件

等组件Child挂载3秒之后我去触发haha事件

代码-效果

只要一触发

他就调用回调函数xyz(),然后就打印

一触发'haha'事件,立马就去调用'xyz'回调函数,然后打印结果xyz

1, 在生命周期函数中使用 emit() 代码-效果

复制代码
// 等待组件挂载完成3秒之后,触发事件haha,传递玩具的名字
    onMounted(()=>{
        setTimeout(()=>{
            emit('haha')
        },3000);
    })

2, 在模板中使用 emit() 代码-效果

点击测试 他就触发回调函数xyz(), 效果如下:

他不仅能触发调用回调函数,还能传递数据,代码-效果

你给Child子组件绑定了一个 haha 事件,haha事件只要被触发,就调用红色的'xyz()'函数,

接下来我们就去触发,当你点击按钮'测试'的时候,@click就执行,只要一执行,触发的是emit('haha',666),代理的是666,666以参数的形式自动注入到xyz(value:number)里面来了

测试 代码-效果

接下来,我们能不能写得再高端一点呢?你给Child组件帮定一个@send-toy发送玩具,完毕就去调用saveToy保存玩具的回调函数

完整代码-效果

打印是在父组件中完成的,此时你已经成功的实现了子传父

代码-效果
刷新的效果
点击 '测试'后的效果
自此,使用emit实现了子传父 自定义事件
send-toy 是肉串命名 这是官方推荐写法
saveToy小驼峰命名 这是官方推荐写法
因此,推荐你始终使用 kebab-case的事件名 即短横线隔开命名规范
_组件通信-方式2-自定义事件 实现代码如下
复制代码
1, src/pages/02_custom-event/Father.vue
<template>
    <div class="father">
        <h3>父组件</h3>
        <h4 v-show="toy">子给父的玩具: {{ toy }}</h4>
        <!-- 给子组件Child绑定事件 -->
        <Child @send-toy="saveToy" />
    </div>
</template>

<script setup lang="ts" name="Father.vue">
import Child from '@/pages/02_custom-event/Child.vue';
import { ref } from 'vue';

// 数据
let toy = ref('');

// 用于保存传递过来的玩具名称的函数或者方法
function saveToy(value: string) {
    console.log('saveToy', value);
    toy.value = value;
}
</script>

<style scoped>
    .father {
        background-color: rgb(165,164,164);
        padding: 20px;
        border-radius: 10px;
    }
    .father button {
        margin-right: 5px;
    }
</style>





2, src/pages/02_custom-event/Child.vue
<template>
    <div class="child">
        <h3>子组件</h3>
        <h4>玩具: {{ toy }}</h4>
        <button @click="emit('send-toy',toy)">测试</button>
    </div>
</template>

<script setup lang="ts" name="Child">
    import { ref,onMounted } from 'vue'
    
    // 数据
    let toy = ref('奥特曼')

    // 声明事件 defineEmits(['toyChanget'])
    const emit = defineEmits(['send-toy'])
    
    // 1, 在生命周期函数中使用 等待组件挂载完成3秒之后,触发事件haha,传递玩具的名字
    // onMounted(()=>{
    //     setTimeout(()=>{
    //         emit('haha')
    //     },3000);
    // })
</script>

<style scoped>
    .child {
        margin-top: 20px;
        background-color: rgb(76,200,76);
        padding: 20px;
        box-shadow: 0 0 10px black;
        border-radius: 10px;
    }
</style>
刷新-效果
点击 '测试'-效果

054_组件通信-方式3-mitt

A组件找到粉色的公共组件,我要给你绑定一个事件abc,事件只要被触发就调用xyz,

B组件也去找到公共组件,恰巧就触发他身上的abc事件,也带一个666过去,他只要一触发abc事件,A组件的abc事件就调用了xyz,不仅被调用,而且还能接收参数666

复制代码
1, pubsub
2, $bus
3, mitt
这三个都是一个套路,
接收数据的: 提前绑定好事件(pubsub提前订阅消息)
提供数据的: 在合适的时候触发事件(pubsub发布消息)

如何使用mitt

mitt 是一个小巧、快速的发布/订阅事件库。它允许你在不同的组件或模块之间进行通信, 而不需要直接引用它们。这对于解耦和保持代码的清晰性非常有帮助。

复制代码
1, 安装 mitt
npm i mitt

2, 引入mitt
import mitt from 'mitt'

3, 调用并且暴露出去
// 调用mitt得到emitter,emitter可以绑定事件,触发事件 创建一个mitt实例
const emitter = mitt();

// 暴露emitter实例,以便在其他文件中使用
export default emitter;

// export default mitt()

在 src/main.ts中引入 emitter

1, emitter.all 拿到所有绑定事件

2, emitter.emit 触发某一个事件

3, emitter.off 解绑某一个事件

4, emitter.on 绑定某一个事件

mitt官网

复制代码
https://github.com/developit/mitt

src/pages/mitt/Child1.vue

复制代码
<template>
    <div class="child1">
        <h3>子组件1</h3>
    </div>
</template>

<script setup lang="ts" name="Child1">

</script>

<style scoped>
    .child1 {
        margin-top: 50px;
        background-color: skyblue;
        padding: 10px;
        box-shadow: 0 0 10px black;
        border-radius: 10px;
    }
    .child1 button {
        margin-right: 10px;
    }
</style>

src/pages/mitt/Child2.vue

复制代码
<template>
    <div class="child2">
        <h3>子组件2</h3>
    </div>
</template>

<script setup lang="ts" name="Child2">

</script>

<style scoped>
    .child2 {
        margin-top: 50px;
        background-color: orange;
        padding: 10px;
        box-shadow: 0 0 10px black;
        border-radius: 10px;
    }
</style>

src/pages/mitt/Father.vue

复制代码
<template>
    <div class="father">
        <h3>父组件</h3>
        <Child1 />
        <Child2 />
    </div>
</template>

<script setup lang="ts" name="Father">
    import Child1 from '@/pages/03_mitt/Child1.vue';
    import Child2 from '@/pages/03_mitt/Child2.vue';
</script>

<style scoped>
    .father {
        background-color: rgb(165,164,164);
        padding: 20px;
        border-radius: 10px;
    }
    .father button {
        margin-left: 5px;
    }
</style>

子组件1要把玩具交给子组件2,如何来实现

子组件1在提供数据 子组件2在接收数据

接收数据---->绑定事件(发布消息)--->emitter.on()

提供数据----->触发事件(订阅消息)--->emitter.emit()

子组件1把玩具给子组件2 完整代码-效果

刷新

点击 '玩具给子组件2' 按钮

不传666, 传toy参数 代码-效果

如何将组件1传递给组件2的数据展示在页面上

完整代码 - 效果

刷新

点击 '玩具给子组件2' 按钮

A组件给B组件传递数据

A组件提供数据: A组件摸到emitter,触发事件[emitter.emit()]

B组件接收数据: B组件也摸到emitter,绑定事件[emitter.on()]

按照以上逻辑,mitt可以实现任意两个组件之间通信的需求

接收数据方 组件卸载时,解绑 send-toy 绑定事件

当组件卸载后,如果不解绑send-toy绑定事件,组件已经不存在, 但是他绑定的事件还存活在,这样对内存有消耗,不友好,所以在组件卸载时,一定要解绑send-toy绑定事件,牢记!牢记!

_组件通信-方式3-mitt 实现代码如下:

复制代码
1, src/pages/03_mitt/Father.vue
<template>
    <div class="father">
        <h3>父组件</h3>
        <Child1 />
        <Child2 />
    </div>
</template>

<script setup lang="ts" name="Father">
    import Child1 from '@/pages/03_mitt/Child1.vue';
    import Child2 from '@/pages/03_mitt/Child2.vue';
</script>

<style scoped>
    .father {
        background-color: rgb(165,164,164);
        padding: 20px;
        border-radius: 10px;
    }
    .father button {
        margin-left: 5px;
    }
</style>




2, src/pages/03_mitt/Child1.vue
<template>
    <div class="child1">
        <h3>子组件1</h3>
        <h4>玩具: {{ toy }}</h4>
        <button @click="emitter.emit('send-toy',toy)">玩具给子组件2</button>
    </div>
</template>

<script setup lang="ts" name="Child1">
import { ref } from 'vue'
import emitter from '@/utils/emitter';
let toy = ref('奥特曼')
</script>

<style scoped>
    .child1 {
        margin-top: 50px;
        background-color: skyblue;
        padding: 10px;
        box-shadow: 0 0 10px black;
        border-radius: 10px;
    }
    .child1 button {
        margin-right: 10px;
    }
</style>





3, src/pages/03_mitt/Child2.vue
<template>
    <div class="child2">
        <h3>子组件2</h3>
        <h4>电脑: {{ computer }}</h4>
        <h4 v-show="toy">收到玩具: {{ toy }}</h4>
    </div>
</template>

<script setup lang="ts" name="Child2">
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
    // console.log('send-toy',value)
})

// 接收数据方 组件卸载时,解绑 send-toy 绑定事件
onUnmounted(()=>{
    emitter.off('send-toy')
})
</script>

<style scoped>
    .child2 {
        margin-top: 50px;
        background-color: orange;
        padding: 10px;
        box-shadow: 0 0 10px black;
        border-radius: 10px;
    }
</style>

刷新后

点击 '玩具给子组件2' 按钮后

055_组件通信-方式4-v-model

在项目实际开发中,很少使用 v-model来做通信,

但是UI组件库大量使用 v-model进行通信

Button是组件 a的值是传给组件的

我们需要知道UI组件库 UI组件库大量使用 v-model进行通信,将v-model掌握好,我们将来就可以看懂UI组件库源代码。

准备两个组件,用于实现v-model自定义组件的双向绑定功能

src/pages/04_v-model/Father.vue

复制代码
<template>
    <div class="father">
        <h3>父组件</h3>
        <input type="text" v-model="username">
    </div>
</template>

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

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

<style scoped>
    .father {
        padding: 20px;
        background-color: rgb(165,164,164);
        border-radius: 10px;
    }
</style>

v-model 是双向绑定

动态绑定的 :value="username" 用于实现数据到页面

可以实现数据到页面 可以修改红色框里面username的值,页面上会呈现修改后的值。

经测试,页面的数据的改变是不可能来到 蓝色方框里面的

绿色绑定实现双向绑定的一条线,红色绑定实现了双向绑定的另一条线

上图就是v-model实现双向绑定的底层原理:

一个动态的value值,配合input事件

红色<HTMLInputElement>就是断言,表示HTML里面的输入性元素,$event是事件对象,target发生事件的本体 .value 拿到他的值

v-model实现双向绑定代码如下

复制代码
<input type="text" v-model="username">

v-model实现双向绑定的底层代码如下

复制代码
<input type="text" :value="username" @input="username=(<HTMLInputElement>$event.target).value">


代码解释
<HTMLInputElement>就是断言,表示HTML里面的输入性元素,$event是事件对象,target发生事件的本体 .value 拿到他的值

绿色的可以实现双向绑定,但是组件 AtguiguInput 虽然可以实现输入,但是他不可以使用v-model实现双向绑定

父组件 与 子组件

Element-UI给我们提供的el-input组件是可以使用v-model实现双向绑定

el-input组件给我们提供的是一个非常漂亮的输入框,并且可以使用v-mode实现双向绑定

Element-UI给el-input身上提供了v-model双向绑定且输入框很漂亮

绿色这条线: 实现数据到页面 :modelValue="username"

vue3的v-model实现双向绑定的写法 update:modelValue 是事件名

复制代码
<AtguiguInput :modelValue="username" @update:modelValue="username = $event" />

绿色部分为红色部分的双向绑定的一条线从数据到页面,黄色部分为红色部分实现双向绑定的另一条线从页面到数据

你只要写第9行,你就是给组件AtguiguInput

传递了:modelValue="username",

绑定了事件名为 update:modelValue 的事件

绿色为传统输入框

第9行的本质就是红色框里面的数据

绿色的为UI底层库做的

红色的是程序员写的

谁是UI组件库谁操心底层的写法

UI组件库的作者就得写粉色框里面的代码

你们公司封装UI组件库也是这么写的

你为公司封装了一套UI组件库,你的基本功绝对上上层

event 如果是传统的DOM事件对象,你就得event.target.value拿到input元素

父组件里面的event是你传递过来的数据,此时就不需要event.target.value

总结:

$event到底是啥?啥时候能.target

对于原生事件,$event就是事件对象====>能 .target

对于自定义事件,$event就是触发事件时,所传递的数据==>不能 .target

v-model 即能父传子,也能子传父

红色框 父传子 粉色框 子传父

vue2

复制代码
<input type="text" v-model="username">


v-model 底层实现原理
<input type="text" :value="username" @input="username=(<HTMLInputElement>$event.target).value">

以上两个效果一样

vue3

复制代码
<AtguiguInput v-model="username" />

<AtguiguInput :modelValue="username" @update:modelValue="username = $event"


<input type="text" :value="modelValue" @input="emit('update:modelValue',(<HTMLInputElement>$event.target).value)">

_组件通信-方式4-v-model(自定义组件实现v-model双向绑定功能),实现代码如下:

复制代码
1, src/pages/04_v-model/Father.vue
<template>
    <div class="father">
        <h3>父组件</h3>
        <!-- v-model用在html标签上时,默认等同于v-bind:value + v-on:input -->
        <!-- <input type="text" v-model="username"> -->

        <!-- 以下两行实现双向绑定效果一样 -->
        <!-- 1, <input type="text" v-model="username"> -->
        <!-- vue2的写法 -->
        <!-- 2, <input type="text" :value="username" @input="username=(<HTMLInputElement>$event.target).value"> -->
        
        <!-- v-model用在html标签上 -->
        <AtguiguInput v-model="username" />
        <!-- vue3的写法 -->
        <!-- <AtguiguInput :modelValue="username" @update:modelValue="username = $event" /> -->
    </div>
</template>

<script setup lang="ts" name="Father">
import { ref } from 'vue'
import AtguiguInput from '@/pages/04_v-model/AtguiguInput.vue'

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

<style scoped>
    .father {
        padding: 20px;
        background-color: rgb(165,164,164);
        border-radius: 10px;
    }
</style>







1, src/pages/04_v-model/AtguiguInput.vue
<template>
    <input type="text" :value="modelValue" @input="emit('update:modelValue',(<HTMLInputElement>$event.target).value)">
</template>

<script setup lang="ts" name="AtguiguInput">
    defineProps(['modelValue'])
    const emit = defineEmits(['update:modelValue'])
</script>

<style scoped>
    input {
        border: 2px solid black;
        background-image: linear-gradient(45deg,red,yellow,green);
        height: 30px;
        font-size: 20px;
        color: white;
    }
</style>

以上代码-自定义组件AtguiguInput可以实现v-model双向通信绑定功能

056_组件通信-v-model的细节

6.4 [v-model]
1, 概述: 实现 父<-->子之间相互通信。
2, 前序知识--v-model的本质
复制代码
<!--使用v-model指令-->
<input type="text" :value="username" @input="username=(<HTMLInputElement>$event.target).value" />

3, 组件标签上的 v-model的本质 :modelValue + update:modelValue事件。

复制代码
<!--组件标签上使用 v-model 指令-->
<AtguiguInput v-model="userName" />

<!--组件标签上 v-model 的本质-->
<AtguiguInput :modelValue="userName" @update:model-value="userName = $event" />

AtguiguInput 组件中:

复制代码
<template>
	<div class="box">
		<!--将接收的value值赋值给input元素的value属性,目的是: 为了呈现数据-->
		<!--给input元素绑定原生input事件,触发input事件时,进行触发update:model-value事件-->
		<input type="text" :value="modelValue" @input="emit('update:model-value',$event.target.value)" />
	</div>
</template>

<script setup lang="ts" name="AtguiguInput">
	// 接收 props
	defineProps(['modelValue'])
	// 声明事件
	const emit = defineEmits(['update:model-value'])
</script>

4, 也可以更换 value,例如改成 abc

复制代码
<!--也可以更换 value,例如改成 abc-->
<AtguiguInput v-model:abc="userName" />

<!--上面代码的本质如下-->
<AtguiguInput :abc="userName" @update:abc="userName = $event" />

AtguiguInput 组件中

复制代码
<template>
	<div class="box">
		<input type="text" :value="abc" @input="emit('update:abc',$event.target.value)" />
	</div>
</template>
<script setup lang="ts" name="AtguiguInput">
	// 接收 props
	defineProps(['abc'])
	// 声明事件
	const emit = defineEmits(['update':'abc'])
</script>

5, 如果 value 可以更换,那么就可以在组件标签上多次使用 v-model

复制代码
<AtguiguInput v-model:abc="userName" v-model:xyz="password" />

笔记

将数据呈现在页面上

又用了一个第三方的组件库

我不用最传统的input,因为他不好看

我想写AtguiguInput,我们自己封装的input输入框

这个username已经双向绑定了

如果我们去修改 v-model="username " 下面的ref('username') 就会发生改变

页面呈现的 {{username}}也会发生变化

v-model="username" 能否改名

名: modelValue 事件: update:modelValue

此时传递的是qwe,再也不是modelValue

所以 在 defineProps(['qwe'])传递的是qwe,不是 modelValue

这里收的 :value="qwe" 也是qwe,而不是modelValue

也就是将所有 'modelValue'都换成'qwe'

update: 是vue3里面的固有形式,你是不能动的

添加一个[v-model:mima="password"]

定义password是响应式数据且为字符串

_组件通信-v-model的细节 实现代码如下:

复制代码
1, src/pages/04_v-model/Father.vue
<template>
    <div class="father">
        <h3>父组件</h3>
        <h4>{{ username }}</h4>
        <h4>{{ password }}</h4>
        <!-- a, v-model用在html标签上时,默认等同于v-bind:value + v-on:input -->
        <!-- <input type="text" v-model="username"> -->

        <!-- 以下两行实现双向绑定效果一样 -->
        <!-- 1, <input type="text" v-model="username"> -->
        <!-- vue2的写法 -->
        <!-- 2, <input type="text" :value="username" @input="username=(<HTMLInputElement>$event.target).value"> -->
        
        <!-- b, v-model用在html标签上 -->
        <!-- <AtguiguInput v-model="username" /> -->
        <!-- vue3的写法 -->
        <!-- <AtguiguInput :modelValue="username" @update:modelValue="username = $event" /> -->
        
        <!-- c, 修改 modelValue -->
        <AtguiguInput v-model:ming="username" v-model:mima="password" />
    </div>
</template>

<script setup lang="ts" name="Father">
import { ref } from 'vue'
import AtguiguInput from '@/pages/04_v-model/AtguiguInput.vue'

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

<style scoped>
    .father {
        padding: 20px;
        background-color: rgb(165,164,164);
        border-radius: 10px;
    }
</style>






2, src/pages/04_v-model/AtguiguInput.vue
<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)">

    <!-- <input type="text" :value="modelValue" @input="emit('update:modelValue',(<HTMLInputElement>$event.target).value)"> -->
</template>

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

<style scoped>
    input {
        border: 2px solid black;
        background-image: linear-gradient(45deg,red,yellow,blue);
        height: 30px;
        font-size: 20px;
        color: white;
    }
</style>

057_组件通信-方式5-$attrs

6, 组件通信

Vue3组件通信和Vue2的区别:
复制代码
1, 移出事件总线,使用 mitt代替。
2,vuex 换成了 pinia。
3, 把 .sync 优化到了 v-model 里面了。
4, 把 $listeners 所有的东西,合并到 $attrs 中了。
5, $children 被砍掉了。
常见搭配形式:
组件关系 传递方式
父传子 1,props
2, v-model
3,$refs
4, 默认插槽,具名插槽

免费公共图床

复制代码
https://imgsha.com/page/buy
https://imgchr.com/
https://z1.ax1x.com/2023/11/19/piNxlo4.jpg
http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4

6.5 [$attrs]

复制代码
1, 概述: $attrs 用于实现当前组件的父组件,向当前组件的子组件通信(祖<-->孙).
2, 具体说明: $attrs 是一个对象,包含所有父组件传入的标签属性。
注意:$attrs会自动排除props中声明的属性(可以认为声明过的props被子组件自己"消费"了)
父组件:
<template>
	<div class="father">
		<h3>父组件</h3>
		<Child :a="a" :b="b" :c="c" :d="d" v-bind="x:100,y:200" :updateA="updateA" />
	</div>
</template>

<script setup lang="ts" name="Father">
	import Child from '.Child.vue'
	import {ref} from 'vue'
	
	let a = ref(1)
	let b = ref(2)
	let c = ref(3)
	let d = ref(4)
	
	function updateA(value) {
		//
	}
</script>

笔记

绿色的给黄色的传递数据

典型的祖孙关系

准备如下三个组件,用于实现祖孙通信

src/pages/05_$attrs/Father.vue

复制代码
<template>
    <div class="father">
        <h3>父组件</h3>
        <Child />
    </div>
</template>

<script setup lang="ts" name="Father">
    import Child from '@/pages/05_$attrs/Child.vue';
</script>

<style scoped>
    .father {
        padding: 20px;
        background-color: rgb(165,164,164);
        border-radius: 10px;
    }
</style>

src/pages/05_$attrs/Child.vue

复制代码
<template>
    <div class="child">
        <h3>Child组件</h3>
        <GrandChild />
    </div>
</template>

<script setup lang="ts" name="Child">
    import GrandChild from '@/pages/05_$attrs/GrandChild.vue';
</script>

<style scoped>
    .child {
        margin-top: 20px;
        background-color: skyblue;
        padding: 20px;
        border-radius: 10px;
        box-shadow: 0 0 10px black;
    }
</style>

src/pages/05_$attrs/GrandChild.vue

复制代码
<template>
    <div class="grand-child">
        <h3>GrandChild组件</h3>
    </div>
</template>

<script setup lang="ts" name="GrandChild">

</script>

<style scoped>
    .grand-child {
        margin-top: 20px;
        background-color: orange;
        padding: 20px;
        border-radius: 10px;
        box-shadow: 0 0 10px black;
    }
</style>

父组件 子组件 孙组件

父组件定义初始数据以及页面展示 代码-效果

复制代码
<template>
    <div class="father">
        <h3>父组件</h3>
        <h4>a: {{ a }}</h4>
        <h4>b: {{ b }}</h4>
        <h4>c: {{ c }}</h4>
        <h4>d: {{ d }}</h4>
        <Child />
    </div>
</template>

<script setup lang="ts" name="Father">
    import Child from '@/pages/05_$attrs/Child.vue';
    import {ref} from 'vue'

    let a = ref(1)
    let b = ref(2)
    let c = ref(3)
    let d = ref(4)
</script>

<style scoped>
    .father {
        padding: 20px;
        background-color: rgb(165,164,164);
        border-radius: 10px;
    }
</style>

给子组件传递props

值是读取a这个响应式数据

通过 props 读取传入的 a 值

我们给子组件多传几个值,props接收的在props里面,其他未接收的就到attrs里面去了,父组件传值给子组件了,子组件为接收的值都跑到attrs里面去了, 代码-效果

父组件给子组件传了四个值,子组件只收了1个值

在绿色框范围内你就无法得到b,c,d的值了

我们传入的值,接收到了,就在props里面,未接收的在attrs里面

v-bind:

复制代码
v-bind="{x:100,y:200}"

v-bind="{x:100,y:200}"相当于 :x="100",:y="200"

也就是说 v-bind 后面其实可以写对象的

对象翻译过来就是这些绿色代码

红色代码和绿色代码是没有差别的,只是呈现是的不一致而与

v-bind里面的数据都以props的形式传递到子组件里面去了

子组件只收到了a,代码-效果

剩余的都在attrs里面

v-model:

父组件给子组件传了如下很多值

子组件一个都不接收,一个都不呈现

工具都不呈现 props

所有的值都在 attrs 里面

父组件将自己所有的数据都交给了子组件

子组件拿到父组件的数据,并将所有的数据直接给了GrandChild组件

子组件只需要写是 v-bind="$attrs"这样一句代码

Child组件就将所有的数据给 GrandChild组件了

Child组件通过props就将所有的数据给 GrandChild组件了

这些所有的props都是 Child组件的父组件Father组件给的数据

去 GrandChild 组件将Father传递的数据在页面是呈现出来 代码-效果

Father组件不仅将a,b,c,d响应式数据传给了GrangChild组件,还把写死的v-bind的数据也传给了GrangChild组件

通过props和attrs就实现了祖<-->孙传递数据

孙组件如何传给祖组件数据呢?

Father父组件传递函数或者方法给Child子组件

Child组件什么都不要改,你里面只要写 : v-bind="$attrs" 就可以了

$attrs 现在是不是多了一个 updateA

GrandChild组件是不是也收到 'updateA'

将 updateA呈现到页面

每点一次就加6,你就传入6

刷新如下

点击按钮,a的值变更为如下图所示

总结:attrs即能实现祖传孙,也能实现孙传祖

_组件通信-方式5-$attrs 实现代码如下

复制代码
1, src/pages/05_$attrs/Father.vue
<template>
    <div class="father">
        <h3>父组件</h3>
        <h4>a: {{ a }}</h4>
        <h4>b: {{ b }}</h4>
        <h4>c: {{ c }}</h4>
        <h4>d: {{ d }}</h4>
        <Child :a="a" :b="b" :c="c" :d="d" v-bind="{x:100,y:200}" :updateA="updateA" />
    </div>
</template>

<script setup lang="ts" name="Father">
    import Child from '@/pages/05_$attrs/Child.vue';
    import {ref} from 'vue'

    let a = ref(1)
    let b = ref(2)
    let c = ref(3)
    let d = ref(4)

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

<style scoped>
    .father {
        padding: 20px;
        background-color: rgb(165,164,164);
        border-radius: 10px;
    }
</style>





2, src/pages/05_$attrs/Child.vue
<template>
    <div class="child">
        <h3>Child组件</h3>
        <GrandChild v-bind="$attrs" />
    </div>
</template>

<script setup lang="ts" name="Child">
    import GrandChild from '@/pages/05_$attrs/GrandChild.vue';
</script>

<style scoped>
    .child {
        margin-top: 20px;
        background-color: skyblue;
        padding: 20px;
        border-radius: 10px;
        box-shadow: 0 0 10px black;
    }
</style>





3, src/pages/05_$attrs/GrandChild.vue
<template>
    <div class="grand-child">
        <h3>GrandChild组件</h3>
        <h4>a: {a}</h4>
        <h4>b: {b}</h4>
        <h4>c: {c}</h4>
        <h4>d: {d}</h4>
        <h4>x: {x}</h4>
        <h4>y: {y}</h4>
        <button @click="updateA(6)">点我将Father组件的a更新</button>
    </div>
</template>

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

<style scoped>
    .grand-child {
        margin-top: 20px;
        background-color: orange;
        padding: 20px;
        border-radius: 10px;
        box-shadow: 0 0 10px black;
    }
</style>
刷新后的效果

点击'点我将Father组件的a值更新' 就是实现孙传祖

058_组件通信-方式6-refs与parent

6.6 [refs, parent]
1, 概述:
复制代码
1, 概述: 
	a,$refs 用于: 父 -> 子 
	b,$parent 用于: 子 -> 父
2, 原理如下: 

2, 原理如下:

属性 说明
$refs 值为对象,包含所有被ref属性标识的DOM元素或组件实例。
$parent 值为对象,当前组件的父组件实例对象。

笔记

6.7 [provide,inject]

1, 概述:实现祖孙组件之间直接通信
2, 具体使用:
复制代码
1, 在祖先组件中通过 provide 配置向后代组件提供数据
2,在后代组件中通过 inject 配置来声明接收数据
3, 具体编码:
复制代码
第一步:父组件中,使用 provide 提供数据

准备组件数据

src/pages/06_refs-parent/Father.vue

复制代码
<template>
    <div class="father">
        <h3>父组件</h3>
        <Child1 />
        <Child2 />
    </div>
</template>

<script setup lang="ts" name="Father">
    import Child1 from '@/pages/06_$refs-$parent/Child1.vue'
    import Child2 from '@/pages/06_$refs-$parent/Child2.vue';
</script>

<style scoped>
    .father {
        padding: 20px;
        background-color: rgb(165,164,164);
        border-radius: 10px;
    }
</style>

src/pages/06_refs-parent/Child1.vue

复制代码
<template>
    <div class="child1">
        <h3>子组件1</h3>
    </div>
</template>

<script setup lang="ts" name="Child1">

</script>

<style scoped>
    .child1 {
        margin-top: 20px;
        background-color: skyblue;
        padding: 20px;
        border-radius: 10px;
        box-shadow: 0 0 10px black;
    }
</style>

src/pages/06_refs-parent/Child2.vue

复制代码
<template>
    <div class="child2">
        <h3>子组件2</h3>
    </div>
</template>

<script setup lang="ts" name="Child2">

</script>

<style scoped>
    .child2 {
        margin-top: 20px;
        background-color: orange;
        padding: 20px;
        border-radius: 10px;
        box-shadow: 0 0 10px black;
    }
</style>

分屏

父组件的数据 代码-效果

子组件1的数据 代码-效果

自己的

子组件2的数据: 代码-效果

自己的

第一个需求: 想在父组件里面去动子组件1里面的玩具 把'奥特曼'改为'小猪佩奇'。该如何来实现呢?

C1为响应式数据,要拿到他的数据得C1.value

红色的地方没有任何的数据,对子组件是一种保护

父组件能随便翻子组件的日记本吗?

第15行代码表示: 子组件1允许父组件看他的玩具和书籍

此时父组件就可以拿到子组件1的数据

典型的父组件给子组件1传数据
刷新后

点击'修改Child1的玩具'按钮

我们全程都用的ref
继续聊ref

ref 就是一个标识, $refs就是一堆标识

父组件用两个子组件

想获取子组件1,C1就可以了,想获取子组件2,C2就可以了

现在我有一种需求: 绿色的父组件想一次把所有的子组件都获取,该如何处理?

$event无非两个答案:1, 事件对象 2, 自定义事件带来的数据

button是普通html标签

@click是普通的DOM事件,所以

所以 $event 必是事件对象

一打印就是事件对象

$refs 他的值是一个对象,对象里面包含着所有的子组件

对象里面包含C1的实例对象,还包含C2的实例对象

C1里面的书籍叫book,C2里面的书籍也叫book

我们来一个循环让子组件1和子组件2都个加两边书

为什么叫C1和C2,是因为你在打标识的时候,你这里使用的是C1和C2

你在获取所有子组件时继续是C1和C2标识

for in 循环

复制代码
for (const key in object) {
    if (Object.prototype.hasOwnProperty.call(object, key)) {
        const element = object[key];

    }
}

对象中的key他就是一个字符串

我们要的是对象里面每一个key的值 refs[key]

代码-效果

refs为事件对象

第一次遍历为C1,第二次遍历为C2

你这个 refs里面不一定要C1,C2,TS开始质疑里面什么都没有,所以

你要将绿色赋值给红色,红色这边必需是确定的

TS认为refs对象可能没有C1,C2

refs确实是一个对象,key确实为字符串,至于里面存放的是什么都可以,我们用any表示

父组件给子组件通信 以上讲的都是refs,refs是拿子组件. 还有一个$parent

还有一个parent,parent是拿父组件

需求:在子组件设置一个按钮,点一下,就干掉父组件一套房产

parent就在Child1组件的父组件

红色表示什么数据都没有拿到,因为你没有得到父组件的许可

去父组件添加如下代码: 就可以拿到父组件的数据

复制代码
// 向外部提供数据 宏函数 暴露给父组件的数据和方法
defineExpose({
    house
})

把 house 交给子组件就可以了

父组件只需要写 defineExpose({house})就相当于父组件同意子组件了

点击 '点击一下父组件的房产就减一'按钮,父组件的房产就减少一套,说明子组件在跟父组件通信。这就是子传父的一种方式: $parent

_组件通信-方式6-refs与parent 实现代码如下:

复制代码
1, src/pages/06_$refs-$parent/Father.vue
<template>
    <div class="father">
        <h3>父组件</h3>
        <h4>房产: {{ house }}</h4>
        <button @click="changeToy">修改Child1的玩具</button>
        <button @click="changeComputer">修改Child2的电脑</button>
        <button @click="getAllChild($refs)">获取所有子组件的实例对象</button>
        <Child1 ref="C1" />
        <Child2 ref="C2" />
    </div>
</template>

<script setup lang="ts" name="Father">
    import Child1 from '@/pages/06_$refs-$parent/Child1.vue'
    import Child2 from '@/pages/06_$refs-$parent/Child2.vue'
    import { ref } from 'vue'

    let C1 = ref()
    let C2 = ref()

    // 数据
    let house = ref(4)

    // 方法
    function changeToy() {
        C1.value.toy = '小猪佩奇'
    }

    function changeComputer() {
        C2.value.computer = '华为'
    }

    function getAllChild(refs:{[key:string]:any}) {
        for (let key in refs) {
            // console.log(refs[key]);
            refs[key].book += 3
        }
    }

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

<style scoped>
    .father {
        padding: 20px;
        background-color: rgb(165,164,164);
        border-radius: 10px;
    }
    .father button {
        margin-right: 10px;
    }
</style>






2, src/pages/06_$refs-$parent/Child1.vue
<template>
    <div class="child1">
        <h3>子组件1</h3>
        <h4>玩具: {{ toy }}</h4>
        <h4>书籍: {{ book }} 本</h4>
        <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) {
        parent.house -= 1
        // console.log(parent)
    }


    // 需要一个宏函数 把数据交给外部组件使用
    defineExpose({
        toy,
        book
    })

    
</script>

<style scoped>
    .child1 {
        margin-top: 20px;
        background-color: skyblue;
        padding: 20px;
        border-radius: 10px;
        box-shadow: 0 0 10px black;
    }
</style>






3, src/pages/06_$refs-$parent/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 scoped>
    .child2 {
        margin-top: 20px;
        background-color: orange;
        padding: 20px;
        border-radius: 10px;
        box-shadow: 0 0 10px black;
    }
</style>

刷新后

点击 '获取所有子组件的实例对象' 父组件按钮,子组件书籍不停增加,通过$refs实现 父组件传递给子组件数据

点击 '点击一下父组件的房产就减一'子组件按钮,父组件的房产数据就不停的减少,通过 $parent 实现 子组件传递给父组件

059_1个注意点

obj 是一个响应式对象,对象里面有三组key-value

a,b 是写死的值, c 是一个响应式数据

c 是不是一个响应式数据?是

c 是放在一个响应式对象里面

当你去触碰obj.c的时候,他自动解包->就是自动开始读取c.value

他自动解包->就是自动开始读取c.value

代码-效果

你得打开他.value的值为4

c 与 x 最本质的区别是: c是在reactive()响应式作用域内

你去触碰 c 的时候,他会自动解包,但是 x 他不能,所以 x必须有 .value 才可以拿到他的值。

因为 refs 本身就是一个响应式对象,他里面读取refs的值就不需要 .value

以下天蓝色框里面的数据也是同理的

parent 就是一个响应式对象

060_组件通信-方式7-provide-inject

专门实现祖孙之间传递数据

祖孙之间直接通信,不打扰中间人

准备组件数据-效果

src/pages/07_provide-inject/Child.vue

复制代码
<template>
    <div class="child">
        <h3>子组件</h3>
        <GrandChild />
    </div>
</template>

<script setup lang="ts" name="Child">
    import GrandChild from '@/pages/07_provide-inject/GrandChild.vue';
</script>

<style scoped>
    .child {
        margin-top: 20px;
        background-color: skyblue;
        padding: 20px;
        border-radius: 10px;
        box-shadow: 0 0 10px black;
    }
</style>

src/pages/07_provide-inject/Father.vue

复制代码
<template>
    <div class="father">
        <h3>父组件</h3>
        <Child />
    </div>
</template>

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

<style scoped>
    .father {
        padding: 20px;
        background-color: rgb(165,164,164);
        border-radius: 10px;
    }
</style>

src/pages/07_provide-inject/GrandChild.vue

复制代码
<template>
    <div class="grand-child">
        <h3>我是孙组件</h3>
    </div>
</template>

<script setup lang="ts" name="GrandChild">

</script>

<style scoped>
    .grand-child {
        margin-top: 20px;
        background-color: orange;
        padding: 20px;
        border-radius: 10px;
        box-shadow: 0 0 10px black;
    }
</style>

父-子-孙

给各个组件添加数据

这个黄色的子组件存在的唯一目的就是想证明:

祖组件与孙组件通信不会打扰子组件的,实现祖组件与孙组件自由通信

祖组件与孙组件如何实现自由通信?

需求:祖组件想把自己的车子和银子都给孙组件,如何来实现

在祖组件使用provide,让祖组件为其后代提供数据

黄色框里面的数据可供他的儿子,孙子,重孙子等使用

我们现在想体验一下不打扰儿子的情况下,祖孙之间直接通信

祖组件提供数据,孙组件只需要注入,就可以使用了

复制代码
1, 在孙组件引入 inject
import {inject} from 'vue'

2, 注入
let x = inject('qian')

3, 在模板中使用
<h4>银子: {{ x }}</h4>

实现祖给孙传递数据 代码-效果

TS默认推断出来,所以有报错提示

我们可以通过添加默认值的方式,让TS推导出来,报错提示解决

provide(参数1: 数据的名字,参数2:数据的具体值)比如provide('money',money),provide('car',car)

inject(参数1:数据的名字,参数2:默认值,参数3: 将默认值设置为工厂值)

祖组件与孙组件之间直接通信

通过 'provide' 和 'inject' 这个两个方法,可以直接实现祖组件传递数据给孙组件

孙组件能否给组组件传递数据呢?

孙->祖 是可以的,那我们该如何来实现呢?

祖组件给孙组件提供了一套{money,updateMoney}

money表示提供了具体多少钱

updateMoney表示提供了更新钱的方法

传的是一个对象

将对象解构出来

实现孙组件传祖组件 代码-效果

传默认值解决TS推断出错提示

复制代码
{money:0,updateMoney:(param:number)=>{}}

函数收到一个值,类型为number

全程我们都没有打开Child.vue文件,你发现祖孙之间可以实现互传数据

子组件终于做回了自己

如何体现祖传孙通信的 祖组件用银子和车子,孙组件也有银子和车子

如何体现孙传祖通信的 孙组件在动祖组件的钱,祖组件的钱也跟着变动

点击孙组件中的'花爷爷的钱'按钮,孙组件的钱变动,祖组件的钱也跟着变动

money:money.value 简写为 money

绿色的为key,红色的为key.value为一个具体的值 还让key失去了响应式

此时孙组件中的money只是一个值,不具备响应式属性

所以此时万万不可用 money.value传值,不 .value你传的是一个ref对象,没有传 .value,你传的是一个响应式数据

_组件通信-方式7-provide-inject 实现代码如下

复制代码
1, src/pages/07_provide-inject/Child.vue
<template>
    <div class="child">
        <h3>子组件</h3>
        <GrandChild />
    </div>
</template>

<script setup lang="ts" name="Child">
    import GrandChild from '@/pages/07_provide-inject/GrandChild.vue';
</script>

<style scoped>
    .child {
        margin-top: 20px;
        background-color: skyblue;
        padding: 20px;
        border-radius: 10px;
        box-shadow: 0 0 10px black;
    }
</style>





2, src/pages/07_provide-inject/GrandChild.vue
<template>
    <div class="grand-child">
        <h3>我是孙组件</h3>
        <h4>银子: {{ money }}万元</h4>
        <h4>车子: 一辆{{ car.brand }}车 - 价值{{ car.price }}万元</h4>
        <button @click="updateMoney(6)">花爷爷的钱</button>
    </div>
</template>

<script setup lang="ts" name="GrandChild">
    import {inject} from 'vue'

    let {money,updateMoney} = inject('moneyContext',{money:0,updateMoney:(param:number)=>{}})
    let car = inject('car',{brand:'未知',price:0})
</script>
<style scoped>
    .grand-child {
        margin-top: 20px;
        background-color: orange;
        padding: 20px;
        border-radius: 10px;
        box-shadow: 0 0 10px black;
    }
</style>





3, src/pages/07_provide-inject/Father.vue

<template>
    <div class="father">
        <h3>父组件</h3>
        <h4>银子: {{ money }}万元</h4>
        <h4>车子: 一辆{{ car.brand }}车 - 价值{{ car.price }}万元</h4>
        <Child />
    </div>
</template>

<script setup lang="ts" name="Father">
    import Child from '@/pages/07_provide-inject/Child.vue'
    import { provide, ref,reactive } from 'vue'

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

    function updateMoney(value: number) {
        money.value -= value
    }

    // 向后代提供数据-->直接向后代注入数据
    provide('moneyContext', {money, updateMoney})
    provide('car', car)
</script>

<style scoped>
    .father {
        padding: 20px;
        background-color: rgb(165,164,164);
        border-radius: 10px;
    }
</style>

刷新后

点击孙组件中的'花爷爷的钱'按钮

相关推荐
木斯佳1 小时前
前端八股文面经大全:有赞前端一面二面HR面(2026-1-13)·面经深度解析
前端·状态模式
VX:Fegn08952 小时前
计算机毕业设计|基于springboot + vue养老院管理系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
码云数智-园园2 小时前
Vue 3 + TypeScript 企业级项目架构实战:从0到1打造可维护的前端工程体系
前端·vue.js·typescript
CappuccinoRose2 小时前
CSS 语法学习文档(十五)
前端·学习·重构·渲染·浏览器
Marshall1512 小时前
DC-SDK 实战指南:基于 Cesium 的三维数字孪生大屏开发 前言 在当今数字孪生、智慧城市等领域的开发中,三维地图可视化已经成为核心需求。
前端
少云清2 小时前
【UI自动化测试】5_web自动化测试 _元素操作和元素信息获取
前端·web自动化测试
lyyl啊辉3 小时前
2. Vue数据双向绑定
前端·vue.js
CappuccinoRose4 小时前
CSS 语法学习文档(十七)
前端·css·学习·布局·houdini·瀑布流布局·csspaintingapi
keyborad pianist4 小时前
Web开发 Day1
开发语言·前端·css·vue.js·前端框架