一、父子组件传值
父组件通过props向子组件传值,子组件通过emit触发自定义事件传递新值给父组件。
props:setup函数中第一个参数props用于接收父组件传递进来的参数。注意:props参数中,只会接收props选项中接收的参数 。
context参数:setup函数中的第二个参数是一个上下文对象context。context参数里面有三个对象:attrs,emit,slots。
- attrs:用于获取没有采用props选项接收的参数。
- emit:用于触发自定义事件。
- slots:返回的是插槽里面的虚拟DOM信息。
虚拟DOM:就是vue实例所能识别的一个DOM信息对象。
父组件
<div class="parent">
<h2>父组件</h2>
<div>姓名:{{ name }}</div>
<div>年龄:{{ age }}</div>
<div>年龄:{{ sex }}</div>
<Son1 :name="name" :age="age" :sex="sex" @updateData="updateData">
<!-- 这里是具名插槽 -->
<template v-slot:top>
<div class="box">这里顶部</div>
<div class="box">
<button>这里顶部</button>
</div>
</template>
<!-- #是v-slot:的简写 -->
<template #bottom="scope">
<div class="box">这里底部
<ul>
<li>{{scope.car.name}}</li>
<li>{{scope.car.price}}</li>
</ul>
</div>
</template>
</Son1>
</div>
import {ref} from 'vue'
import Son1 from "./Son1.vue";
export default {
name: "Parent",
setup() {
let name = ref('张三')
let age = ref(20)
let sex = ref('男')
let updateData = (e)=>{
name.value = e.myName
age.value = e.myAge
sex.value = e.mySex
}
return {
name,
age,
sex,
updateData
}
},
/* data() {
return {
name:'张三',
age:20
}
},
methods: {
updateData(e){
this.name = e.myName
this.age = e.myAge
}
}, */
components: {
Son1,
},
};
子组件
<div class="son1">
<!-- 插槽,定义多个插槽时,需要给插槽定义名称:具名插槽 -->
<slot name="top"></slot>
<h2>Son1</h2>
<div>姓名:{{ myName }}</div>
<div>年龄:{{ myAge }}</div>
<div>性别:{{ mySex }}</div>
<div><button @click="updateData">修改数据</button></div>
<!-- 可以通过插槽传递一份数据给插槽的使用者,这样的插槽称为作用域插槽 -->
<slot name="bottom" :car="car"></slot>
</div>
import {ref,reactive} from 'vue';
export default {
name: "Son1",
//接收父组件的传值
props: ["name", "age"],
// setup的第一个参数,用于获取父组件的传值
// 注意:props选项接收了几个参数,setup函数的第一个参数就只能获取几个参数。
// setup的第二个参数,是一个上下文对象;它里面一个方法是emit,用于触发自定义事件
// props选项没有接收的传值,在setup里面通过上下文对象的attrs属性接收
setup(props,{emit,attrs,slots}) {
// slots对象返回的是插槽里面的虚拟DOM信息
console.log(slots.top());
// 中转props里面的数据,因为props是只读的
let myName = ref(props.name)
let myAge = ref(props.age)
let mySex = ref(attrs.sex)
let car = reactive({
name:'奔驰',
price:20
})
let updateData = ()=>{
myName.value = '李四'
myAge.value = 30
mySex.value = '女'
// 触发自定义事件
emit('updateData',{myName:myName.value,myAge:myAge.value,mySex:mySex.value})
}
return {
myName,
myAge,
mySex,
updateData,
car
}
}
/* data() {
return {
myName:this.name,
myAge:this.age
}
},
methods: {
updateData(){
this.myName = '李四'
this.myAge = 30
this.$emit('updateData',{myName:this.myName,myAge:this.myAge})
}
}, */
};
二、祖孙组件传值
祖级组件通过provide将指定的数据添加为依赖数据,让后代组件可以直接使用。孙代组件通过inject注入祖级组件中设置为依赖的数据。
祖级组件
import {ref,provide} from 'vue'
import Son from './components/Son.vue'
export default {
name: 'App',
setup() {
let name = ref('张三')
let age = ref(20)
// 通过provide()方法,定义依赖数据,从此它的子组件,就可以获取这些数据了
provide('name',name)
provide('age',age)
return {
name,
age
}
},
components: {
Son
}
/* data() {
return {
name:'张三',
age:20
}
},
methods: {
// 修改数据的方法
updateData(name,age){
this.name = name
this.age = age
}
},
// 定义依赖数据
provide(){
return {
name:this.name,
age:this.age,
updateData:this.updateData
}
}, */
}
孙级组件
<div class="subSon">
<h2>SubSon</h2>
<ul>
<li>姓名:{{name}}</li>
<li>年龄:{{age}}</li>
<li>
<button @click="update">修改信息</button>
</li>
</ul>
</div>
import {inject} from 'vue'
export default {
name: "SubSon",
setup() {
// inject()方法,用于注入父级中依赖的数据
let name = inject('name')
let age = inject('age')
let update = ()=>{
name.value = '李四'
age.value = 30
}
return {
name,
age,
update
}
}
/* inject:['name','age','updateData'],
data() {
return {
myName:this.name,
myAge:this.age
}
},
methods: {
update(){
this.myName = '李四'
this.myAge = 30
this.updateData('李四',30)
}
}, */
};
三、v-model
在Vue3中,父组件中可以通过v-model指令实现对多个数据的双向绑定。注意:vue3取消了sync修饰符,它将v-model指令和sync修饰符进行了合并。
子组件中,自定义事件名称必须命名为update:属性名,就可以实现对父组件中指定属性的双向绑定。
父级组件
<div class="app">
<h2>App</h2>
<!-- 在vue3中子定义组件时,v-model可以使用多次,实现对多个数据的双向绑定-->
<Son3 v-model:planeName="planeName" v-model:planePrice="planePrice" v-model:planeAddress="planeAddress" />
</div>
import {ref,provide} from 'vue'
import Son3 from './components/Son3.vue'
export default {
name: 'App',
setup() {
//定义飞机的相关数据
let planeName = ref('波音747')
let planePrice = ref(100)
let planeAddress = ref('美国')
return {
// 返回飞机相关信息
planeName,
planePrice,
planeAddress
}
},
components: {
Son3
}
子级组件
<div class="son3">
<h2>Son3</h2>
<ul>
<li>飞机名称:{{ planeName }}</li>
<li>飞机价格:{{ planePrice }}</li>
<li>飞机产地:{{ planeAddress }}</li>
<li>
<button @click="updatePlaneName">修改飞机名称</button>
</li>
<li>
<button @click="updatePlanePrice">修改飞机价格</button>
</li>
<li>
<button @click="updatePlaneAddress">修改飞机产地</button>
</li>
</ul>
</div>
export default {
name: "Son3",
//接收父组件传递过来的数据
props: ["planeName", "planePrice", "planeAddress"],
setup(props, { emit }) {
let updatePlaneName = () => {
// 注意:事件方法必须是update:prop,如果父组件中采用的是v-model:prop
// 此时,父组件就可以实现对prop的双向数据绑定。
emit("update:planeName", "长城1号");
};
let updatePlanePrice = () => {
emit("update:planePrice", 200);
};
let updatePlaneAddress = () => {
emit("update:planeAddress", "中国");
};
return {
updatePlaneName,
updatePlanePrice,
updatePlaneAddress,
};
},
};
四、异步组件
suspense内置组件:用于在渲染异步组件时,添加Loading效果。
使用 <suspense></suspense> 包裹所有异步组件相关代码。
<suspense></suspense> 下 <template #default></template> 插槽包裹异步组件。
<suspense></suspense> 下 <template #fallback></template> 插槽包裹渲染异步组件之前的内容。
注意:异步加载的组件可以用suspense,也可以不用。不用suspense组件,会失去异步的作用;但是,如果组件中setup的返回值是一个Promise对象,该组件必须要用suspense。
定义组件
<div class="son4">
<h2>Son4</h2>
<ul>
<li>商品名称:{{ goodsName }}</li>
<li>商品价格:{{ goodsPrice }}</li>
<li>
<input type="text" v-model="goodsName">
</li>
</ul>
</div>
import { ref } from "vue";
export default {
name: "Son4",
setup() {
let goodsName = ref("小米电视");
let goodsPrice = ref(2000);
//setup方法的返回值,可以是一个Promise对象
return new Promise((resolve,reject)=>{
setTimeout(() => {
resolve({
goodsName,
goodsPrice,
show
})
}, 2000);
})
// return {
// goodsName,
// goodsPrice,
// }
},
};
使用
<div class="app">
<h2>App</h2>
<!-- suspense内置组件,用于在渲染异步组件时,添加Loading效果 -->
<suspense>
<!-- default插槽里面放置异步组件 -->
<template #default>
<Son4/>
</template>
<!-- fallback插槽里面放置组件没有加载完成时显示的内容 -->
<template #fallback>
Loading...
</template>
</suspense>
</div>
// defineAsyncComponent组合式API,用于定义异步组件
import {defineAsyncComponent} from 'vue'
// 异步导入组件
const Son4 = defineAsyncComponent(()=>import('./components/Son4.vue'))
五、teleport组件
Vue 3.0 新增了一个内置组件 teleport ,主要是为了解决以下场景:有时组件模板的一部分逻辑上属于该组件,而从技术角度来看,最好将模板的这一部分移动到 DOM 中 Vue app 之外的其他位置。
teleport组件:瞬移。通过to属性确定里面的元素移动到哪。to属性的属性值是指定的选择器。
例如:在下面的案例中,box盒子逻辑上属于son4盒子,但是为了使box盒子设置的相对定位,不受其他父级元素的影响,我们将box盒子瞬移到body下。
<div class="son4">
<h2>Son4</h2>
<button @click="show=true">显示</button>
<!-- teleport组件:瞬移。通过to属性确定里面的元素移动到哪。
to属性的属性值是指定的选择器 -->
<teleport to="body">
<div v-show="show" class="box">
<ul>
<li>商品名称:{{ goodsName }}</li>
<li>商品价格:{{ goodsPrice }}</li>
</ul>
<button @click="show=false">关闭</button>
</div>
</teleport>
</div>
.box{
width: 200px;
height: 200px;
border: 1px solid black;
padding: 10px;
background: lightblue;
/* 绝对定位 */
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
使用teleport瞬移后,box盒子的结构是在body下,与#app平级。