JaneChelle | Blog JaneChelle | Blog

执着于理想,纯粹于当下

目录
父子组件通信
/    

父子组件通信

我们知道子组件是不能引用父组件或者Vue实例的数据的。但是,在开发中,往往一些数据确实需要从上层传递到下层:比如在一个页面中,我们从服务器请求到了很多的数据。其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。这个时候,并不会让子组件再次发送一个网络请求 ,而是直接让大组件(父组件)将数据传递给小组件(子组件)

一、如何进行父子组件间的通信呢? Vue官方提到

  • 通过props向子组件传递数据
  • 通过事件向父组件发送消息(emit)

image.png

二、 在组件中,使用选项props来声明需要从父级接收到的数据。

props的值有两种方式:

  • 方式一 :字符串数组,数组中的字符串就是传递时的名称。
  • 方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。

我们说过,除了数组之外,我们也可以使用对象,当需要对props进行类型等验证时,就需要对象写法了。

验证都支持哪些数据类型呢?

  • String
  • Number
  • Boolean
  • Symbol
  • Function
  • Date
  • Object
  • Array

当我们有自定义构造函数时,验证也支持自定义的类型。

Vue.component('my-component',
        {
            props: {
                //基础的类型检查( 'null’匹配任何类型)
                propA: Number ,
                //多个可能的类型
                propB: [string, Number],
               //必填的字符串
                propc: {
                    type: string,
                    required: true
                },
              //带有默认值的数字
                propD: {
                    type: Number,
                    default: 100
                },
              //带有默认值的对象
                propE: {
                    type: object,
                    //对象或数组默认值必须从-个工厂函数获取
                   default: function() {
                        return {message: 'he1lo'}
                    }
                },
               //自定义验证函数
                propF: {
                    validator: function(value) {
                        //这个值必须匹配下列字符串中的一个
                        return [' success', 'warning', ' danger'].indexOf(value) !== -1
                    }
                }
            }
        })
// 自定义类型
        function Person(firstName, lastName){
            this.firstName = firstName;
            this.lastName = lastName;
        }
        Vue.component('blog-post', {
            props: {
                author: Person
            }
        })

三、父传子中props中的驼峰标识

当props中变量用驼峰命名时,那么父组件向子组件传值时,必须化为短线 “-”,例如:

   // 父传子 props
    const cpn = {
        template: '#cpn',
        props: {
            cMessage: {
                type: String,
                default: ' ',
                required: true
            }
        },
        data(){
            return{}
        }
    }
    const app = new Vue({
        el: '#app',
        data: {
            message: '你好',
        },
        components: {
            cpn: cpn
        }
    })

那么用子组件传值的时候,应该这样写:

<div id="app">
    <cpn :c-message="message"></cpn>
</div>

四、子传父(自定义事件)

一种比较常见的是 子组件 传递数据或事件到父组件中,这个时候,我们需要使用自定义事件来完成。

什么时候需要自定义事件呢?

  • 当子组件需要向父组件传递数据时,就要用到自定义事件了。
  • 我们之前学习的v-on不仅仅可以用于监听DOM事件,也可以用于组件间的自定义事件。

自定义事件的流程:

  • 在子组件中,通过$emit()来触发事件。
  • 在父组件中,通过v-on来监听子组件事件。

案例-点击加减:

<!--父组件模板-->
<div id="app">
    <cpn @add-click="add" @sub-click="sub"></cpn>
    <H2>{{counter}}</H2>
</div>
<!--子组件模板-->
<template id="cpn">
    <div>
        <button @click="increament"> +1 </button>
        <button @click="decreament"> -1 </button>
    </div>
</template>
// 子组件
    const cpn = {
        template: '#cpn',
        methods: {
            increament() {
                // 发射,向外派发给父组件
                this.$emit('add-click')
            },
            decreament() {
                this.$emit('sub-click')
            }
        }
    }
    // 父组件
    const app = new Vue({
        el: '#app',
        data(){
            return{
                counter: 0
            }
        },
        components:{
            cpn
        },
        methods: {
            add(){
                this.counter++;
            },
            sub(){
                this.counter--;
            }
        }
    })

案例二:父组件中引用upload子组件,当上传文件的时候,需要把上传的文件值传递给父组件。这里就用到了$emit()事件进行派发

子组件中:

<template>
    <div>
        <div class="file">{{tip}}
            <input type="file" @change="change" :accept="accept">
        </div>
    </div>
</template>
<script>
export default {
    props: {
        tip:{
            type: String,
            default: '上传文件',
        },
        taskId: {
          type: Number
        },
        accept: {
          type: String
        },
    },
    created(){},
    methods: {
      change(){
          this.$emit('updateFile',event);
        },
    }
  };
</script>
<style lang="scss">
.el-button{
  padding: 9px 15px;
}
.file {
    position: relative;
    display: inline-block;
    background: #2C68FF;
    border: 1px solid #99D3F5;
    border-radius: 4px;
    padding: 4px 12px;
    overflow: hidden;
    color: #ffffff;
    text-decoration: none;
    text-indent: 0;
    line-height: 20px;
}
.file input {
    position: absolute;
    font-size: 100px;
    right: 0;
    top: 0;
    opacity: 0;
}
.file:hover {
    background: #5686ff;
    border-color: #5686ff;
    color: #ffffff;
    text-decoration: none;
}
</style>

这个change方法,通过$emit("暴露在父组件中绑定的方法名 updateFile",需要传递给父组件的值: event);把这个值——上传的文件,派发给父组件。

在父组件中:

<template>
    <div>
        <div class="file">{{tip}}
            <upload :tip="instructions" :accept="docAccept" ref="document" :taskId="taskId" @updateFile="changeIn"/>
        </div>
    </div>
</template>
<script>
export default {
    data(){
        return{
            instructions: '上传任务说明书',
            docAccept: '.pdf,.zip,.rar,.doc,.docx',
            taskId: 0,
            document: '',
        };
    methods: {
       changeIn(event){
          this.document = event.target.files[0];
        },
    }
  };
</script>

PS: 第二个案例中props和$emit其实都用到了,可以自行理解。


标题:父子组件通信
作者:JaneChelle
地址:https://xiao.algerfan.cn/articles/2020/08/03/1596443369597.html