1.v-model
Vue3可以通过v-model指令实现对多个数据的双向绑定
<!-- 父级 -->
<template>
<Five v-model:bg="bg" v-model:mb="mb" v-model:nn="nn" @click="appClick"></Five>
</template>
<script>
import { ref } from 'vue'
import Five from './components/Five.vue'
export default {
name: 'App',
components: {
Five
},
setup() {
//饼干
let bg = ref(5)
//面包
let mb = ref(8)
//牛奶
let nn = ref(3)
let appClick = (e)=>{
alert(e)
}
return{
bg,
mb,
nn,
appClick
}
}
}
</script>
<!-- 子级 -->
<template>
<div class="five">
<h3>Five</h3>
<div class="item">
饼干:<button @click="bg--">-</button><input readonly type="text" :value="bg"><button @click="bg++">+</button>
</div>
<div class="item">
面包:<button @click="mb--">-</button><input readonly type="text" :value="mb"><button @click="mb++">+</button>
</div>
<div class="item">
牛奶:<button @click="nn--">-</button><input readonly type="text" :value="nn"><button @click="nn++">+</button>
</div>
<button @click="fiveClick">点击</button>
</div>
</template>
<script>import { ref, watch } from "vue";
export default {
// 接收属性
props:['bg','mb','nn'],
// 放在自定义事件名称跟原生事件名称同名
// 这里统一注册自定义事件的名称,
// 这样父组件只会触发子组件自定义的事件,不会触发原生事件
emits:['click','update:bg','update:mb','update:nn'],
setup(props,{emit}) {
//获取父组件的传值
let bg = ref(props.bg)
let mb = ref(props.mb)
let nn = ref(props.nn)
watch(bg,(val)=>{
// 注意:事件名称是update:属性名称
emit('update:bg',val)
})
watch(mb,(val)=>{
emit('update:mb',val)
})
watch(nn,(val)=>{
emit('update:nn',val)
})
let fiveClick = ()=>{
// 当我们的自定义事件,跟原生事件重名时,会执行两次
emit('click','哈哈!我被点击了!')
}
return {
bg,
mb,
nn,
fiveClick
}
}
};
2.组件传值
1.父子组件传值
父级
<template>
<div class="app">
<div class="box">
<div>汽车名称:{{carName}}</div>
<div>汽车价格:{{carPrice}}</div>
<div>汽车颜色:{{carColor}}</div>
<div>汽车产地:{{carAddress}}</div>
<!-- 通过props给子组件传值 -->
<Three :car-name="carName" :car-price="carPrice"
:car-color="carColor" :car-address="carAddress"
@change-name="changeName" @change-price="changePrice"
@change-color="changeColor" @change-address="changeAddress"></Three>
</div>
</div>
</template>
<script>
import Three from './components/Three.vue'
import { ref } from 'vue'
export default {
name: 'App',
components: {
Three
},
setup() {
let carName = ref('奔驰')
let carPrice = ref(20)
let carColor = ref('红色')
let carAddress = ref('德国')
let changeName = (e)=>{
carName.value = e
}
let changePrice = (e)=>{
carPrice.value = e
}
let changeColor = (e)=>{
carColor.value = e
}
let changeAddress = (e)=>{
carAddress.value = e
}
return{
carName,
carPrice,
carColor,
carAddress,
changeName,
changePrice,
changeColor,
changeAddress
}
}
}
</script>
子级
<template>
<div class="three">
<h3>子组件</h3>
<!-- 注意:props接过来的属性,在模板中可以直接修改 -->
<div>汽车名称:{{ myCarName }}<button @click="setCarName">修改车名</button></div>
<div>汽车价格:{{ myCarPrice }}<button @click="setCarPrice">修改价格</button></div>
<div>汽车颜色:{{ myCarColor }}<button @click="setCarColor">修改颜色</button></div>
<div>汽车产地:{{ myCarAddress }}<button @click="setCarAddress">修改地址</button></div>
</div>
</template>
<script>
import { toRef } from "vue";
export default {
// props接过来的属性在方法里面是只读的
props: ["carName", "carPrice", "carColor"],
// 使用vue3中的组合式API写法
setup(props,context) {
// props返回的是一个Proxy对象,里面保存的是props选项接的属性
// context对象里面保存了三个重要属性:attrs,emit,slots
// attrs 相当于 this.$arrts
// emit 相当于 this.$emit
// slots 是插槽信息
let myCarName = toRef(props,'carName') //ref(props.carName)
let myCarPrice = toRef(props,'carPrice')
let myCarColor = toRef(props,'carColor')
let myCarAddress = toRef(context.attrs,'car-address')
let setCarName = ()=>{
myCarName = '宝马'
context.emit('change-name',myCarName)
}
let setCarPrice = ()=>{
myCarPrice = 30
context.emit('change-price',myCarPrice)
}
let setCarColor = ()=>{
myCarColor = '蓝色'
context.emit('change-color',myCarColor)
}
let setCarAddress = ()=>{
myCarAddress = '美国'
context.emit('change-address',myCarAddress)
}
return{
myCarName,
myCarPrice,
myCarColor,
myCarAddress,
setCarName,
setCarPrice,
setCarColor,
setCarAddress
}
}
};
</script>
2.隔代组件传值
父级
setup() {
//定义数据
let name = ref('张三')
let age = ref(20)
//汽车数据
let car = reactive({
name:'大众',
color:'黑色'
})
//修改汽车的名称
let updateCarName = (val)=>{
car.name = val
}
//修改汽车的价格
let updateCarColor = (val)=>{
car.color = val
}
// 定义依赖数据
// provide方法的第一个参数是key,第二个参数是value
provide('name',name)
provide('age',age)
// 将两个修汽车信息的方法,作为依赖数据传出去
provide('updateCarName',updateCarName)
provide('updateCarColor',updateCarColor)
return{
name,
age,
car,
}
}
子级
<template>
<div class="two">
<h3>Two</h3>
<div>姓名:{{ myName }}</div>
<div>年龄:{{ myAge }}</div>
<button @click="updateName">修改姓名</button>
<button @click="updateAge">修改年龄</button>
<button @click="updateCarName('比亚迪')">修改汽车名称</button>
<button @click="updateCarColor('黄色')">修改汽车颜色</button>
</div>
</template>
<script>
import { inject } from "vue";
export default {
setup() {
// 通过inject注入父级的依赖数据集
let myName = inject("name");
let myAge = inject("age");
let updateName = () => {
myName.value = "李四";
};
let updateAge = () => {
myAge.value = "王五";
};
// 通过inject注入两个方法
let updateCarName = inject("updateCarName");
let updateCarColor = inject("updateCarColor");
return {
myName,
myAge,
updateName,
updateAge,
updateCarName,
updateCarColor,
};
},
};
</script>
3.兄弟组件传值
1.下载mitt :npm i mitt
2.main.js中导入mitt
import { createApp } from 'vue'
import App from './App.vue'
//导入插件mitt(第三方中央事件中线库)
import mitt from "mitt"
//创建一个Vue实例
let app = createApp(App)
//挂载事务总线为全局属性
// config.globalProperties 就相当于返回Vue的原型对象
app.config.globalProperties.$bus = new mitt()
//将Vue实例挂载到#app容器中
app.mount('#app')
3. 监听事件
<script>
// getCurrentInstance方法,用于在setup函数中获取当前组件实例
import {ref,getCurrentInstance} from 'vue'
export default {
// vue3推崇的写法
setup() {
// 获取当前组件实例 不等同于this
let $this = getCurrentInstance()
// $this.appContext 获取当前项目中的Vue实例
let dogName = ref('琪琪')
let dogSex = ref('女生')
//监听事件
$this.appContext.config.globalProperties.$bus.on('update-dog-name',(e)=>{
dogName.value = e
})
$this.appContext.config.globalProperties.$bus.on('update-dog-sex',(e)=>{
dogSex.value = e
})
return {
dogName,
dogSex
}
}
}
</script>
4.触发事件
<template>
<div class="four">
<h3>Four</h3>
<button @click="updateDogName">修改狗狗昵称</button>
<button @click="updateDogSex">修改狗狗性别</button>
</div>
</template>
<script>
import {getCurrentInstance} from 'vue'
export default {
setup() {
//获取当前组件实例
let $this = getCurrentInstance()
let updateDogName = ()=>{
$this.appContext.config.globalProperties.$bus.emit('update-dog-name','天天')
}
let updateDogSex = ()=>{
$this.appContext.config.globalProperties.$bus.emit('update-dog-sex','男生')
}
return {
updateDogName,
updateDogSex
}
}
};
</script>
3.异步组件
<template>
<div class="home">
<h1>首页</h1>
<One></One>
<!-- suspense内置组件,用于加载异步组件,添加loading效果 -->
<suspense>
<!-- default插槽里面放置异步组件 -->
<template #default>
<Two></Two>
</template>
<!-- fallback插槽里面制作loading效果 -->
<template #fallback>
Loading...
</template>
</suspense>
<!-- 异步组件,可以不用suspense,
但是如果异步组件内部的setup函数返回的是一个Promise对象,
此时的异步组件就必须要使用suspense -->
<suspense>
<template #default>
<Three></Three>
</template>
<template #fallback>
Loading...
</template>
</suspense>
<!-- <Three></Three> -->
</div>
</template>
<script>
// defineAsyncComponent方法,用于异步加载组件
import {defineAsyncComponent} from 'vue'
import One from '../components/One.vue'
// import Two from './components/Two.vue'
// 异步导入Two组件和Three
let Two = defineAsyncComponent(()=>import('../components/Two.vue'))
// 注意:如果组件中setup方法返回的是Promise对象,那么组件必须要采用异步的方式加载
// import Three from './components/Three.vue'
let Three = defineAsyncComponent(()=>import('../components/Three.vue'))
export default {
name: 'App',
components: {
One,
Two,
Three
}
}
</script>
4.teleport组件
<button @click="show=true">显示</button>
<!-- teleport组件:官方起的名称:瞬移。通过to属性确定里面的元素移动到哪 -->
<teleport to="body">
<div v-show="show" class="box">
<button @click="show=false">关闭</button>
<div>{{dog}}</div>
</div>
</teleport>