1 组件通信方式
在 Vue 中,组件通信是指多个组件之间传递数据或消息的方式。
由于每个组件的数据是独立的,它们不能直接访问其他组件的数据,因此需要使用一些特定的方式来实现数据传递和通信。
【组件通信方式】
1. 父子组件通信:父组件和子组件之间的数据传递。
2. 非父子组件通信:比如兄弟组件或跨组件的通信。
2 父子组件通信
在 Vue 中,父子组件的通信主要有两种方式:
- 通过 props 传递数据
- 通过 $emit 发送事件
【父子通信流程图】
- 父组件通过 props 将数据传递给子组件
- 子组件利用 $emit 通知父组件修改更新
父组件
【示例1】
父组件App.vue
<template><div class="app" style="border: 3px solid #000; margin: 10px">我是APP组件 <Son></Son></div>
</template><script>
import Son from './components/Son.vue'
export default {name: 'App',data() {return {myTitle: 'Hello Vue!',}},components: {Son,},
}
</script><style>
</style>
子组件Son.vue
<template><div class="son" style="border:3px solid #000;margin:10px">我是Son组件</div>
</template><script>
export default {name: 'Son-Child',
}
</script><style></style>
运行结果:
1. 父组件通过 props
向子组件传递数据
①给子组件以添加属性的方式传值
②子组件内部通过props接收
③渲染使用
运行结果:
【解析】
- 父组件传值: 在父组件 App.vue 中,使用
:myTitle="myTitle"
将父组件的数据 myTitle 传递给子组件 Son.vue。 - 子组件接收: 在 Son.vue 中,子组件通过
props
接收父组件传递的数据,并在模板中展示。
2. 子组件通过 $emit
通知父组件
① $emit
触发事件,给父组件发送消息通知
②父组件监听事件
③提供处理函数,写明处理逻辑
运行结果:
点击“修改title”:
【解析】
- 子组件触发事件: 在子组件 Son.vue 中,点击按钮会触发
changeFn
方法,进而调用this.$emit(changeTitle', '修改后的标题')
,通知父组件 App.vue。 - 父组件监听事件: 在父组件 App.vue 中,使用
@changeTitle="handleChanged"
来监听子组件的changeTitle
事件,并通过handleChanged
方法处理事件,修改父组件的 myTitle 数据。
【示例2】
父组件App.vue
<template><div class="app"><UserInfo></UserInfo></div>
</template><script>
import UserInfo from './components/UserInfo.vue'
export default {data() {return {username: '小帅',age: 28,isSingle: true,car: {brand: '宝马',},hobby: ['篮球', '足球', '羽毛球'],}},components: {UserInfo,},
}
</script><style>
</style>
子组件UserInfo.vue
<template><div class="userinfo"><h3>我是个人信息组件</h3><div>姓名:</div><div>年龄:</div><div>是否单身:</div><div>座驾:</div><div>兴趣爱好:</div></div>
</template><script>
export default {}
</script><style>
.userinfo {width: 300px;border: 3px solid #000;padding: 20px;
}
.userinfo > div {margin: 20px 10px;
}
</style>
运行结果:
优化:
运行结果:
3 props校验
1. 定义
props
是父组件向子组件传递数据的主要方式。为了确保传递的数据类型正确并满足特定条件,可以为 props
定义校验规则。
2. 作用
- 传递数据时,通过校验可以保证数据的类型正确性和格式符合预期
- 如果传递了错误的数据类型或缺少必传属性,Vue 会在开发环境下提供警告
3. 特点
- 支持任意数量的 prop:每个组件可以接收多个 prop。
- 支持任意类型的 prop:可以传递任意数据类型的值(如字符串、数组、对象、布尔值等)。
- 可定义默认值、类型、是否必传等校验规则。
4. 语法
①类型校验
props : {校验的属性名 : 类型
}
【示例】
父组件App.vue
<template><div class="app"><BaseProgress :w="width"></BaseProgress></div>
</template><script>
import BaseProgress from './components/BaseProgress.vue'
export default {data() {return {width: 30,}},components: {BaseProgress,},
}
</script><style>
</style>
子组件BaseProgress.vue
<template><div class="base-progress"><div class="inner" :style="{ width: w + '%' }"><span>{{ w }}%</span></div></div>
</template><script>
export default {/* 无校验 props: ['w'],*/// props校验props:{w:Number}
}
</script><style scoped>
.base-progress {height: 26px;width: 400px;border-radius: 15px;background-color: #272425;border: 3px solid #272425;box-sizing: border-box;margin-bottom: 30px;
}
.inner {position: relative;background: #379bff;border-radius: 15px;height: 25px;box-sizing: border-box;left: -3px;top: -2px;
}
.inner span {position: absolute;right: 0;top: 26px;
}
</style>
运行结果:
如果传入错误的参数:
运行结果:
②完整写法
props : {校验的属性名 : {type : 类型,required : true, // 是否必填default : 默认值, // 默认值validator(value){// 自定义校验逻辑return 是否通过校验}}
}
【示例】
<script>
export default {// 完整写法(类型、默认值、非空、自定义校验)props: {w: {type: Number,//required: true,default: 0,validator(val) {// console.log(val)if (val >= 100 || val <= 0) {console.error('传入的范围必须是0-100之间')return false} else {return true}},},},
}
</script>
如果 w=200,运行结果:
【注意】
① default
和 required
一般不同时写(因为当有必填项时,肯定是有值的)
② default
后面如果是简单类型的值,可以直接写默认。如果是复杂类型的值,则需要以函数的形式 return
一个默认值
【示例】
简单类型的默认值:
props: {name: {type: String,default: '默认名字' // 直接写默认值},age: {type: Number,default: 18 // 直接写默认值},isSingle: {type: Boolean,default: true // 直接写默认值}
}
复杂类型的默认值:
props: {car: {type: Object,default() {return { brand: '宝马', model: 'X5' }; // 使用函数返回默认值}},hobbies: {type: Array,default() {return ['篮球', '足球', '羽毛球']; // 使用函数返回默认值}}
}
4 单向数据流
在 Vue 中,数据流动的方向通常是单向的,即从父组件流向子组件,该特性确保了组件的封装性和可维护性。
无论是 data
还是 props
,都可以为组件提供数据,但是它们的来源和使用方式有所不同。
【区别】
data
的数据是组件自身的,可以随意修改props
的数据是外部传递的,子组件只能读取,不能直接修改,必须遵循单向数据流的原则,即数据从父组件流向子组件。
【单向数据流】
- 父组件通过
props
向子组件传递数据。 - 如果父组件更新了
props
,这个变化会反映在子组件中,子组件的数据会被更新。 - 子组件不能直接修改
props
,而是通过事件或其他方式向父组件发送请求,父组件再更新自己的数据,从而触发子组件的重新渲染。
【示例】
父组件App.vue
<template><div class="app"><BaseCount></BaseCount></div>
</template><script>
import BaseCount from './components/BaseCount.vue'
export default {components:{BaseCount},data(){},
}
</script><style></style>
子组件BaseCount.vue
<template><div class="base-count"><button @click="count--">-</button><span>{{ count }}</span><button @click="count++">+</button></div>
</template><script>
export default {// 自己的数据随便修改 (谁的数据 谁负责)data () {return {count: 100,}}
}
</script><style>
.base-count {margin: 20px;
}
</style>
运行结果:
如果子组件尝试直接修改数据:
运行结果:
该错误是由于子组件直接修改了传入的 prop
数据 count
,而在 Vue 中,props
是只读的,不能在子组件中直接修改。这导致 ESLint 报错,提示 “Unexpected mutation of ‘count’ prop”。
【优化】
【解析】
- 在父组件中,
count
是父组件的状态,子组件通过props
接收到count
。 - 当点击
+
或-
按钮时,子组件更新count
(通过handleAdd
或handleSub
),但由于props
是只读的,子组件不能直接修改它。 - 于是,子组件通过
this.$emit('changeCount', newCount)
向父组件发送事件,告诉父组件新的count
值。 - 父组件通过监听这个事件 (
@changeCount="handleChange"
) 来接收新值,并通过handleChange(newCount)
方法更新父组件的count
数据。 - 更新后的
count
会通过props
传递到子组件,形成一个新的数据流,子组件会重新渲染,显示最新的count
值。
运行结果:
减1:
加1: