一、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、最终效果(父组件向子组件传余额,以及修改药剂的函数)
二、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
3.2、新建src/utils/emitter.ts
文件,创建mitt
实例并暴露
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、效果图
四、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、效果图。(相同的数据但以不同的样式展示。)
五、自定义事件
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、效果图。(点击子组件中的按钮可对父组件中的车数据进行修改)
六、$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、效果图。(通过孙组件中的按钮可修改父组件中的数据,实现祖孙通信)
七、$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、效果图。(通过父组件和子组件中各自的按钮实现父子组件互相通信)
八、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、效果图。(无需通过'子组件'进行数据过渡,直接实现祖孙通信)
九、v-model
9.1、父组件App.vue
。(v-model
的本质::moldeValue
+ update: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.