m一、父组件传递给子组件使用Props
什么是Props呢?
-
Props是你可以在组件上注册一些自定义的attribute;
-
父组件给这些attribute赋值,子组件通过attribute的名称获取到对应的值;
Props有两种常见的用法:
方式一:字符串数组,数组中的字符串就是attribute的名称;
方式二:对象类型,对象类型我们可以在指定attribute名称的同时,指定它需要传递的类型、是否是必须的、默认值等等;
我用下面的综合案例来讲解组件之间的传递方式(该案例用到了props的数组用法)
首先创建一个MusicItem子组件
<template>
<div class="box">
<img :src="coverImgUrl" width="70px">
<p>{{name}}</p>
<button @click="filter()">删除</button>
</div>
</template>
<script>
export default {
name: 'MusicItem',
props:["coverImgUrl","name","id"],
data(){
return {
}
},
methods:{
filter(){
this.$emit("filter");
}
}
}
</script>
<style scoped>
</style>
其次创建一个MusicList组件
<template>
<div class="box" v-for="(it,index) in playlists">
<MusicItem :coverImgUrl="it.coverImgUrl":name="it.name":id="it.id" @filter="filter(index)"/>
</div>
</template>
<script>
import MusicItem from './MusicItem.vue';
export default {
name: '',
data(){
return {
playlists:"",
}
},
methods:{
filter(index){
this.playlists.splice(index,1)
}
},
components:{
MusicItem
},
created() {
fetch("http://39.103.151.139:8000/music/playlist")
.then((data) => data.json())
.then((res) => {
this.playlists = res.playlists;
});
},
}
</script>
<style scoped>
</style>
最后在App.vue文件中引用上面两个组件
<template>
<div class="box">
</div>
</template>
<script>
import MusicItem from './components/MusicItem.vue';
import MusicList from './components/MusicList.vue';
export default {
name: '',
data(){
return {
}
},
methods:{
},
components:{
MusicItem,
MusicList
}
}
</script>
<style scoped>
</style>
Props的对象用法
数组用法中我们只能说明传入的attribute的名称,并不能对其进行任何形式的限制,接下来我们来看一下对象的写法是如何让我们的props变得更加完善的。
当使用对象语法的时候,我们可以对传入的内容限制更多:
比如指定传入的attribute的类型
比如指定传入的attribute是否是必传的
比如指定没有传入时,attribute的默认值
props:{
name:{
type:String,
required:true,
default:'无名氏'
},
age:{
type:Number,
required:true,
},
},
细节
-
那么type的类型都可以是哪些呢? String Number Boolean Array Object Date Function Symbol
-
Prop 名字格式
如果一个 prop 的名字很长,应使用 camelCase 形式,因为它们是合法的 JavaScript 标识符,可以直接在模板的表达式中使用,也可以避免在作为属性 key 名时必须加上引号。
export default {
props: {
personSex: String
}
}
<p>性别:{{personSex}}</p>
虽然理论上你也可以在向子组件传递 props 时使用 camelCase 形式 (使用 DOM 模板时例外),但实际上为了和 HTML attribute 对齐,我们通常会将其写为 kebab-case 形式:
<Person name="李四" age="22" person-sex="男"/>
对于组件名我们推荐使用 PascalCase,因为这提高了模板的可读性,能帮助我们区分 Vue 组件和原生 HTML 元素。然而对于传递 props 来说,使用 camelCase 并没有太多优势,因此我们推荐更贴近 HTML 的书写风格。
单向数据流
所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递。这避免了子组件意外修改父组件的状态的情况,不然应用的数据流将很容易变得混乱而难以理解。
另外,每次父组件更新后,所有的子组件中的 props 都会被更新到最新值,这意味着你不应该在子组件中去更改一个 prop。若你这么做了,Vue 会在控制台上向你抛出警告:
export default { props: ['foo'], created() { // ❌ 警告!prop 是只读的! this.foo = 'bar' } }
导致你想要更改一个 prop 的需求通常来源于以下两种场景:
-
prop 被用于传入初始值;而子组件想在之后将其作为一个局部数据属性。在这种情况下,最好是新定义一个局部数据属性,从 props 上获取初始值即可:
-
需要对传入的 prop 值做进一步的转换。在这种情况中,最好是基于该 prop 值定义一计算属性
-
对象类型的其他写法
export default { props: { // 基础类型检查 //(给出 `null` 和 `undefined` 值则会跳过任何类型检查) propA: Number, // 多种可能的类型 propB: [String, Number], // 必传,且为 String 类型 propC: { type: String, required: true }, // Number 类型的默认值 propD: { type: Number, default: 100 }, // 对象类型的默认值 propE: { type: Object, // 对象或者数组应当用工厂函数返回。 // 工厂函数会收到组件所接收的原始 props // 作为参数 default(rawProps) { return { message: 'hello' } } }, // 自定义类型校验函数 propF: { validator(value) { // The value must match one of these strings return ['success', 'warning', 'danger'].includes(value) } }, // 函数类型的默认值 propG: { type: Function, // 不像对象或数组的默认,这不是一个工厂函数。这会是一个用来作为默认值的函数 default() { return 'Default function' } } } }
二、 子组件传递给父组件数据使用$emit
什么情况下子组件需要传递内容到父组件呢?
-
当子组件有一些事件发生的时候,比如在组件中发生了点击,父组件需要切换内容
-
子组件有一些内容想要传递给父组件的时候
我们如何完成上面的操作呢?
-
首先,我们需要在子组件中定义好在某些情况下触发的事件名称; 自定义事件
-
其次,在父组件中以v-on的方式传入要监听的事件名称,并且绑定到对应的方法中
-
最后,在子组件中发生某个事件的时候,根据事件名称触发对应的事件
该用法请参考上面代码里面的$emit用法