此方法适用于父子组件传参,本文主要内容包括简单的父子传参实例,介绍props接收传参和attr接收参数的关系和区别
官网释义
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
这里有两种常见的试图变更一个 prop 的情形:
- 这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。在这种情况下,最好定义一个本地的 data property 并将这个 prop 用作其初始值:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
- 这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
注意在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变变更这个对象或数组本身将会影响到父组件的状态。
大概意思就是不要试图修改父组件传过来的参数,不然会报错,你应该自己本地存一份。对于传过来的对象类数据,比如数组和对象,修改他们会影响到父组件的参数,因为他们都是指向同一内存地址。不理解这个概念可以先去弄懂深拷贝和浅拷贝
代码实例
父组件
<!-- 父组件 -->
<template>
<div class="family">
<div>
<div>father_string:{{father_string}}</div>
<p>修改父组件的father_string</p>
<input v-model="father_string">
</div>
<!-- 子组件 -->
<Son :father_string="father_string"></Son>
<!-- 子组件 -->
</div>
</template>
<script>
import Son from "./Son";
export default {
name: "HelloWorld",
data () {
return {
father_string: "father",
};
},
components: {
Son,
},
};
</script>
<style scoped>
.family {
width: 40%;
margin: 0 30%;
display: flex;
justify-content: space-around;
}
</style>
子组件
<!-- 子组件 -->
<template>
<div>
<div>子组件接受到的father_string</div>
<div>{{father_string}}</div>
</div>
</template>
<script>
export default {
name: "Son",
props: {
father_string: {
type: String,
default: 'father'
}
}
};
</script>
实例效果
在父组件中的input输入内容改变传参
右侧的father变成了father_changed,可以证明这是一种响应式传参的方式
prop校验
当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。
props传参可以对参数进行复杂的校验
props: {
father_string: {
type: String,
default: 'father',
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return value == 'father'
}
}
}
除了给子组件约定props的传参方式,当开发者预料之外的参数需要传递给子组件时,就可以使用
this.$attrs在子组件中接收,就像下面这样子
<Son :father_string="father_string" :new_attr="'hellow'"></Son>
father_string是我们约定好的字符串,新添加的参数new_attr并没有在子组件内进行props约定,这时候可以直接通过调用this.$attrs调用没有通过props约定的参数,例如下面直接在标签里调用
<div>{{$attrs.new_attr}}</div>
通过props约定好的参数可以在this和this.$props中查看注意:当在props声明了约定的参数后,在this.$attrs中就无法查看到,反之亦然。
即通过在props里约定好的参数在this定义域和this.$props里面查看,而没有约定的参数则通过this.$attrs查看
emit
子向父传参使用this.$emit(),如下
<!-- 在子组件中声明一个点击时间触发,fromson是和父组件的约定,字符串hellow_father是要传递的信息 -->
methods: {
emit_msg () {
this.$emit('fromson', 'hellow_father')
}
}
<!-- 在父组件中使用约定的fromson,并且带上@号,在本地声明一个fromson函数接受并处理传递过来的参数 -->
<Son :father_string="father_string" @fromson="fromson"></Son>
methods: {
fromson (msg) {
<!-- 约定的函数和触发的函数尽量同命,以便处理,msg就是从子组件传递过来的参数 -->
console.log(msg);
}
}
技巧与注意: 使用this.listeners可以查看使用emit约定的方法,但是在Vue3中移除了listeners
sync修饰符
如果父组件的参数需要通过子组件来修改,子组件这里写一个emit,父组件那边写一个函数接受参数来改变对应的值,来回要写两个函数,切来切去未免太麻烦,以下通过使用sync修饰符,节省了父组件这边写一个函数更新数据的步骤
<!-- 在子组件中声明一个点击时间触发,fromson是和父组件的约定,字符串hellow_father是要传递的信息 -->
methods: {
emit_msg () {
this.$emit('update:father_string', 'hellow_father')
}
}
<!-- 注意这里的:father_string.sync="father_string" -->
<Son :father_string.sync="father_string"></Son>
sync会被自动扩展为
<comp :father_string="father_string" @update:father_string="val => father_string = val"></comp>
inheritAttrs属性
默认情况下,子组件内部有一个属性 inheritAttrs: true。 inherit意为继承,Attrs指代属性,继承属性的意思,指代父组件向子组件传递的参数显示在子组件根元素上,设置这个参数就会取消这种默认行为
总结
props和emit是我们平时运用比较多的传参方式,且为响应式,尽量使用props进行参数声明,有必要时候进行校验。