【props】
概述
props
是一种常用的通信方式,常用于父组件->子组件
代码实现
父组件:
<template>
<Child :hobby="hobby" :job="job"></Child>
<p>{{like}}</p>
</template>
<script setup>
import { ref } from 'vue';
import Child from './components/child.vue';
let hobby = '我爱打台球'
let job = '软件工程师'
</script>
<style scoped>
</style>
子组件:
<template>
<h2>爱好:{{ hobby }}</h2>
<h2>职业:{{ job }}</h2>
<button @click="ziChuanFu">点击一下</button>
</template>
<script setup>
import { defineProps , ref } from 'vue'
// 定义props方式一
const props = defineProps({
hobby: {
type: String,
default: '喜欢旅游'
}
})
// 定义props方式二
const props = defineProps(['hobby','job'])
</script>
<style scoped></style>
【emit】
概述
emit
是自定义事件,用于子组件->父组件
代码实现
父组件:
<template>
<Child @sendLike="getLike"></Child>
<p>{{like}}</p>
</template>
<script setup>
import { ref } from 'vue';
import Child from './components/child.vue';
let like = ref('')
const getLike = (val) => {
like.value = val
}
</script>
<style scoped>
</style>
子组件:
<template>
<button @click="ziChuanFu">点击一下</button>
</template>
<script setup>
import { defineEmits, ref } from 'vue'
let like = ref('篮球')
const emits = defineEmits('ziChuanFu')
const ziChuanFu = () => {
emits('sendLike',like)
}
</script>
<style scoped></style>
【v-model】
概述
v-model
可以实现父子组件相互通信
组件标签上v-model的本质是:modelValue + update:modelValue事件
代码实现
父组件:
<template>
<p>父组件项目已运行:{{day}}天</p>
<button @click="addParentDay">点击加一下</button>
<Child v-model:day="day"></Child>
</template>
<script setup>
import { ref } from 'vue';
import Child from './components/child.vue';
let day = ref(1)
const addParentDay = () => {
day.value += 1
}
</script>
<style scoped>
</style>
子组件:
<template>
<p>子组件项目已运行:{{ day }}天</p>
<button @click="addChildDay">点击加一下</button>
</template>
<script setup>
const props = defineProps(['day'])
const emit = defineEmits(['update:day']); //其中day是指父组件的标签中的day
const addChildDay = () => {
emit('update:day', props.day + 1);
}
</script>
<style scoped></style>
【mitt】
概述
mitt
可以用于任意组件间的通信(包括非父子组件、兄弟组件)
安装mitt
npm i mitt
新建文件
src\utils\emitter.ts
// 引入mitt
import mitt from "mitt";
// 创建emitter
const emitter = mitt()
/*
// 绑定事件
emitter.on('abc',(value)=>{
console.log('abc事件被触发',value)
})
emitter.on('xyz',(value)=>{
console.log('xyz事件被触发',value)
})
setInterval(() => {
// 触发事件
emitter.emit('abc',666)
emitter.emit('xyz',777)
}, 1000);
setTimeout(() => {
// 清理事件
emitter.all.clear()
}, 3000);
*/
// 创建并暴露mitt
export default emitter
接受数据的组件:
<template>
<p>{{like}}</p>
</template>
<script setup>
import { ref,onUnmounted } from 'vue';
import emitter from './utils/mitt';
let like = ref('')
// 绑定事件
emitter.on('getLike',(val)=>{
like.value = val
})
onUnmounted(()=>{
// 解绑事件
emitter.off('getLike')
})
</script>
<style scoped>
</style>
提供数据的组件:
<template>
<button @click="handleClick">点击一下</button>
</template>
<script setup>
import { ref } from 'vue'
import emitter from '../utils/mitt';
let like = ref('篮球')
const handleClick = () => {
emitter.emit('getLike',like.value)
}
</script>
<style scoped></style>
注:使用mitt作为组件通信的时候,必须要导出共享实例
【$attrs】
概述
$attrs
用于实现当前组件的父组件,向当前组件的子组件通信(祖→孙)。
具体说明:$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 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){
a.value = value
}
</script>
子组件:
<template>
<div class="child">
<h3>子组件</h3>
<GrandChild v-bind="$attrs"/>
</div>
</template>
<script setup name="Child">
import GrandChild from './GrandChild.vue'
</script>
孙组件:
<template>
<div class="grand-child">
<h3>孙组件</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(666)">点我更新A</button>
</div>
</template>
<script setup name="GrandChild">
defineProps(['a','b','c','d','x','y','updateA'])
</script>
【$refs、parent】
-
概述:
$refs
用于 :父→子。$parent
用于:子→父。
-
原理如下:
属性 说明 $refs
值为对象,包含所有被 ref
属性标识的DOM
元素或组件实例。$parent
值为对象,当前组件的父组件实例对象。
【provide、inject】
概述
可以实现祖孙组件直接通信
具体使用
在祖先组件中通过`provide`配置向后代组件提供数据
在后代组件中通过`inject`配置来声明接收数据
代码实现
【第一步】父组件中,使用`provide`提供数据
<template>
<div class="father">
<h3>父组件</h3>
<h4>钱包余额:{{ money }}</h4>
<h4>帕拉梅拉:{{ car }}</h4>
<button @click="money += 1">钱包余额+1</button>
<button @click="car.price += 1">豪车价格+1</button>
<Child/>
</div>
</template>
<script setup name="Father">
import Child from './Child.vue'
import { ref,reactive,provide } from "vue";
let money = ref(100)
let car = reactive({
brand:'奔驰',
price:100
})
// 用于更新money的方法
function updateMoney(value){
money.value += value
}
// 提供数据
provide('moneyContext',{money,updateMoney})
provide('car',car)
</script>
注意:子组件中不用编写任何东西,是不受到任何打扰的
【第二步】孙组件中使用inject
配置项接受数据。
<template>
<div class="grand-child">
<h3>孙组件</h3>
<h4>资产:{{ money }}</h4>
<h4>汽车:{{ car }}</h4>
<button @click="updateMoney(6)">点我</button>
</div>
</template>
<script setup name="GrandChild">
import { inject } from 'vue';
// 注入数据
let {money,updateMoney} = inject('moneyContext',{
money:0,
updateMoney => {}
})
let car = inject('car')
</script>
【pinia】
安装 pinia
`npm install pinia`
在main.js中引入
import { createApp } from 'vue'
import App from './App.vue'
/* 引入createPinia,用于创建pinia */
import { createPinia } from 'pinia'
/* 创建pinia */
const pinia = createPinia()
const app = createApp(App)
/* 使用插件 */{}
app.use(pinia)
app.mount('#app')
这时开发者工具中已经有了pinia
选项(小菠萝标志)
新建str/store/user.js
// 引入defineStore用于创建store
import { defineStore } from 'pinia'
// 定义并暴露一个store
export const useUserInfo = defineStore('userInfo', {
// 动作
actions: {
changeUsername(value) {
this.username = value
}
},
// 状态
state() {
return {
username: '',
sex: '男',
}
},
// 计算
getters: {}
})
父组件:
<template>
<h1>父组件用户名:{{ userStore.username }}</h1>
<button @click="updateName">修改子组件用户名</button>
<Child></Child>
</template>
<script setup>
import Child from './components/child.vue'
import { useUserInfo } from './store/user.js'
const userStore = useUserInfo()
const updateName = () => {
userStore.changeUsername('张三') // 调用 action 修改状态
}
</script>
<style scoped>
</style>
子组件:
<template>
<h2>子组件用户名{{ userStore.username }}</h2>
<button @click="updateName">修改父组件用户名</button>
</template>
<script setup>
import { useUserInfo } from '../store/user.js'
const userStore = useUserInfo()
const updateName = () => {
userStore.changeUsername('李四') // 调用 action 修改状态
}
</script>
<style scoped></style>
插槽
默认插槽
概述
默认插槽(Default Slot) 是一种组件插槽机制,允许父组件向子组件传递内容,并在子组件的指定位置渲染这些内容
代码实现
父组件:
<template>
<Child title="今日热门歌曲">
<ul>
<li v-for="item in musicList" :key="item.id">{{ item.name }}</li>
</ul>
</Child>
</template>
<script setup>
import { ref } from 'vue'
import Child from './components/child.vue';
const musicList = ref([
{ id: 1, name: '歌曲1' },
{ id: 2, name: '歌曲2' }
])
</script>
子组件:
<template>
<div class="item">
<h3>{{ title }}</h3>
<!-- 默认插槽 -->
<slot></slot>
</div>
</template>
<script setup>
defineProps({
title: String
})
</script>
具名插槽
概述
具名插槽是 Vue 3 中一种允许你在组件中定义多个插槽位置,并可以精确控制内容分发到特定位置的机制。与默认插槽不同,具名插槽通过名称来区分不同的插槽区域
代码实现
父组件:
<template>
<Child title="今日热门歌曲">
<template #s1>
<ul>
<li v-for="item in musicList" :key="item.id">{{ item.name }}</li>
</ul>
</template>
<template #s2>
<a href="/music" target="_blank">更多</a>
</template>
</Child>
</template>
<script setup>
import { ref } from 'vue'
import Child from './components/child.vue';
const musicList = ref([
{ id: 1, name: '歌曲1' },
{ id: 2, name: '歌曲2' }
])
</script>
子组件:
<template>
<div class="item">
<h3>{{ title }}</h3>
<slot name="s1"></slot>
<slot name="s2"></slot>
</div>
</template>
<script setup>
defineProps({
title: String
})
</script>
作用域插槽
概述
数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定
代码实现
父组件:
<template>
<Music v-slot="params">
<ul>
<li v-for="item in params.musicList" :key="item.id">{{ item.name }}</li>
</ul>
</Music>
</template>
<script setup>
import Music from './components/child.vue'
</script>
<style scoped>
</style>
子组件:
<template>
<div class="category">
<h2>今日流行歌曲</h2>
<slot :musicList="musicList"></slot>
</div>
</template>
<script setup name="Music">
import {reactive} from 'vue'
let musicList = reactive([
{id:'01',name:'富士山下'},
{id:'02',name:'寂寞烟火'},
{id:'03',name:'一路生花'},
{id:'04',name:'花海'}
])
</script>