Vue 组件通信方式之一 props和$emit

1,226 阅读3分钟

此方法适用于父子组件传参,本文主要内容包括简单的父子传参实例,介绍props接收传参和attr接收参数的关系和区别

官网释义

所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。

额外的,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。

这里有两种常见的试图变更一个 prop 的情形:

  1. 这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。在这种情况下,最好定义一个本地的 data property 并将这个 prop 用作其初始值:
props: ['initialCounter'],
data: function () {
  return {
    counter: this.initialCounter
  }
}
  1. 这个 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>

实例效果

3.png

在父组件中的input输入内容改变传参

4.png

右侧的father变成了father_changed,可以证明这是一种响应式传参的方式

prop校验

当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。

props传参可以对参数进行复杂的校验

  props: {
        father_string: {
            type: String,
            default: 'father',
            validator: function (value) {
                // 这个值必须匹配下列字符串中的一个
                return value == 'father'
            }
        }
    }

5.png

除了给子组件约定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.props可以查看父向子传递的参数,使用this.props可以查看父向子传递的参数,使用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指代属性,继承属性的意思,指代父组件向子组件传递的参数显示在子组件根元素上,设置这个参数就会取消这种默认行为

从父组件带到子组件根部的属性如下所示从父组件带到子组件根部的属性如下所示

1-5.png

总结

props和emit是我们平时运用比较多的传参方式,且为响应式,尽量使用props进行参数声明,有必要时候进行校验。