一、Vue组件通信
- 每个组件都有自己的数据, 提供在data中, 每个组件的数据是独立的, 组件数据无法互相直接访问 (合理的)
- 但是如果需要跨组件访问数据, 就需要用到组件通信
- 要是有一万个商品????就要写一万个吗?
- 函数调用:看起来调用时用一个函数,执行结果效果都是不一样? 设置形参?在调用的时候传入实参?
特别函数:
1、设置组组件内所有特别,形参
2、组件调用的时候,传入所谓的实参?
(一)组件通信—父传子
1. 组件通信 - 父传子 props 传值
- 父传子的基本语法:
- ①、父组件通过给子组件加属性传值
<!-- 3.所谓调用函数:传实参 -->
<cpt-4 title="超级好吃的口水鸡" price="50" num="8"></cpt-4>
<cpt-4 title="泰国榴莲" price="288" num="6"></cpt-4>
- ②、子组件中, 通过props属性接收
props:["title","price","num"]//1. 设置形参,语法固定
- 创建
components/04-cpt.vue
子组件文件,子组件中, 通过props属性接收。
<template>
<div class="box">
<!-- 2. 使用:形参用到时候,当做变量!类似data初始化内数据变量! -->
<h3>标题:{{ title }}</h3>
<p>价格:{{price}}元</p>
<p>开业大酬宾,全场{{num}}折</p>
</div>
</template>
<script>
export default {
// 特别封装:如何设置所谓的形参
props:["title","price","num"]//1. 设置形参,语法固定
}
</script>
<style scoped>
.box{
border:1px solid black;
}
</style>
App.vue
父组件里面调用cpt4子组件,父组件通过给子组件加属性传值。
<template>
<div>
<h1>超市商品列表:</h1>
<!-- 3.所谓调用函数:传实参 -->
<cpt-4 title="超级好吃的口水鸡" price="50" num="8"></cpt-4>
<cpt-4 title="泰国榴莲" price="288" num="6"></cpt-4>
</div>
</template>
<script>
import cpt4 from "./components/04-cpt.vue"
export default {
components:{
cpt4,
}
}
</script>
- 在data里面传递数据,通过实参来传递形参的值。
<template>
<div>
<h1>超市商品列表:</h1>
<!-- 👇👇👇绑定变量,其中实参名:title和形参的变量名title没有必然的联系 -->
<cpt-4 :title="title" :price="price" :num="num"></cpt-4>
</div>
</template>
<script>
import cpt4 from "./components/04-cpt.vue"
export default {
components:{
cpt4,
},
data(){
return{
title:"火鸡面",
price:10,
num:5
}
}
}
</script>
- 利用父传子,可以往子组件传递数据。
- 父传子的基本步骤是什么?
父组件内, 给子组件添加属性的方式传值
子组件内, 通过 props 接收
2. v-for 遍历展示组件练习
- 以后的数据,都是从后端来的,那如何渲染的呢? 我们可以循环的使用组件吗?要是可以使用,那又如何往组件里面传值呢?
- 需求: 遍历展示商品列表
- ①、假定, 发送请求回来的商品数据,
list:[
{id:1,title:"good-1",price:10,num:8},
{id:2,title:"good-2",price:88,num:8},
{id:3,title:"good-3",price:99,num:8},
]
- ②、v-for 遍历展示
<template>
<div>
<h1>超市商品列表:</h1>
<!-- 商品数据:后台给的list数组 -->
<cpt-4 v-for="item in list" :key="item.id" :title="item.title" :price="item.price" :num="item.num"></cpt-4>
</div>
</template>
<script>
import cpt4 from "./components/04-cpt.vue"
export default {
components:{
cpt4,
},
data(){
return{
title:"火鸡面",
price:10,
num:5,
list:[
{id:1,title:"good-1",price:10,num:8},
{id:2,title:"good-2",price:88,num:8},
{id:3,title:"good-3",price:99,num:8},
]
}
}
}
</script>
3. 单向数据流
- 在vue中需要遵循单向数据流原则: (从父到子的单向数据流动, 叫单向数据流)
在vue中需要遵循单向数据流原则
1. 父组件的数据发生了改变,子组件会自动跟着变
2. 子组件不能直接修改父组件传递过来的props props是只读的
- 如果父组件传给子组件的是一个对象,子组件修改对象的属性,是不会报错的,,,,也应该避免
(二)组件通信—子传父
- 创建
components/04-cpt.vue
子组件文件。
<template>
<div class="box">
<!-- 2使用:形参用到时候,当做变量!类似data初始化内数据变量! -->
<h3>标题:{{ title }}</h3>
<p>价格:{{price}}元</p>
<p>开业大酬宾,全场{{num}}折</p>
<h4>库存:{{ kc }}件</h4>
<button @click="kc--">卖一件</button>
</div>
</template>
<script>
export default {
props:["title","price","num","kc"]//1.设置形参,语法固定
}
</script>
<style scoped>
.box{
border:1px solid black;
}
</style>
App.vue
父组件里面调用cpt4子组件,父组件通过给子组件加属性传值。
<template>
<div>
<h1>超市商品列表:</h1>
<!-- 商品数据:后台给的list数组 -->
<p v-for="item in list" :key="item.id" >{{ item.title }} 库存:{{ item.kc }}</p>
<hr>
<cpt-4
v-for="item in list" :key="item.id" :title="item.title" :price="item.price" :num="item.num" :kc="item.kc"></cpt-4>
</div>
</template>
<script>
import cpt4 from "./components/04-cpt.vue";
export default {
components:{
cpt4,
cpt5,
},
data(){
return{
title:"火鸡面",
price:10,
num:5,
list:[
{id:1,title:"good-1",price:10,num:8,kc:10,},
{id:2,title:"good-2",price:88,num:8,kc:33,},
{id:3,title:"good-3",price:99,num:8,kc:66,},
]
}
}
}
</script>
- 卖一件:
①、子组件自己kc- -
②、报错:数据是父级给的,如果要修改数据,应该是让父级去修改数据
规定:子组件不能直接去修改父组件传入的数据!不然会造成数据紊乱!
1. 子传父组件通信
- 子传父基本语法
- ①、子组件可以通过
this.$emit('事件名', 参数1, 参数2, ...)
触发事件的同时传参的 - 创建
04-cpt.vue
子组件,内置封装好方法emit发射this.$emit("通道名称,要发送的数据,...)
<template>
<div class="box">
<h3>标题:{{ title }}</h3>
<p>价格:{{price}}元</p>
<p>开业大酬宾,全场{{num}}折</p>
<h4>库存:{{ kc }}件</h4>
<button @click="fn">卖一件</button>
</div>
</template>
<script>
export default {
// 特别封装:如何设置所谓的形参
props:["title","price","num","kc","id"],//1.👈👈👈👈设置形参id
methods:{
fn(){
// 👇👇👇👇👇发送:内置封装好方法emit发射this.$emit("通道名称,要发送的数据,...)
this.$emit("aaa",this.id);
}
}
}
</script>
<style scoped>
.box{
border:1px solid black;
}
</style>
- ②、父组件给子组件注册一个自定义事件、父组件可以给子组件设置相应的接受通道名称
<template>
<div>
<h1>超市商品列表:</h1>
<!-- 商品数据:后台给的list数组 -->
<p v-for="item in list" :key="item.id" >{{ item.title }} 库存:{{ item.kc }}</p>
<hr>
<cpt-4
v-for="item in list"
:key="item.id"
:title="item.title"
:price="item.price"
:num="item.num"
:kc="item.kc"
:id="item.id"
@aaa="fnn"
></cpt-4>
<!-- @aaa="fnn"👉👉👉👉👉@通道名称="执行函数" 执行函数(形参){拿到发送过来的数据}-->
</div>
</template>
- ③、父组件并提供对应的函数接收参数
<script>
export default {
methods:{
fnn(id){
console.log(id);
}
},
}
</script>
App.vue
父组件里面调用cpt4子组件,父组件通过给子组件加属性传值。
<template>
<div>
<h1>超市商品列表:</h1>
<!-- 商品数据:后台给的list数组 -->
<p v-for="item in list" :key="item.id" >{{ item.title }} 库存:{{ item.kc }}</p>
<hr>
<cpt-4
v-for="item in list"
:key="item.id"
:title="item.title"
:price="item.price"
:num="item.num"
:kc="item.kc"
:id="item.id"
@aaa="fnn"
></cpt-4>
<!-- @通道名称="执行函数" 执行函数(形参){拿到发送过来的数据}-->
</div>
</template>
<script>
import cpt4 from "./components/04-cpt.vue";
export default {
components:{
cpt4,
},
methods:{
fnn(id){
console.log(id);
}
},
data(){
return{
title:"火鸡面",
price:10,
num:5,
list:[
{id:1,title:"good-1",price:10,num:8,kc:10,},
{id:2,title:"good-2",price:88,num:8,kc:33,},
{id:3,title:"good-3",price:99,num:8,kc:66,},
]
}
}
}
</script>
(三)props 校验
- props 是父传子, 传递给子组件的数据, 为了提高 子组件被使用时 的稳定性, 可以进行props校验, 验证传递的数据是否符合要求。
- 默认的数组形式, 不会进行校验, 如果希望校验, 需要提供对象形式的 props
- 创建
05-cpt.vue
子组件,设置形参以及对象配置的要求。
<template>
<div>
<h1>数量:{{ num }}</h1>
</div>
</template>
<script>
export default {
props:{
//形参名称:对象配置有要求
num:{
type:Number,//数字类型
require:true,//必传项
default:100,//默认值
}
}
}
</script>
App.vue
父组件里面调用cpt5子组件,父组件通过给子组件加属性传值。
<template>
<div>
<h1>超市商品列表:</h1>
<cpt-5 num="800"></cpt-5>
</div>
</template>
<script>
import cpt5 from "./components/05-cpt.vue";
export default {
components:{
cpt5,
},
}
</script>
- props 提供了多种数据验证方案,例如:
- 基础的类型检查 Number
- 多个可能的类型 [String, Number]
- 必填项校验 required: true
- 默认值 default: 100
- 自定义验证函数