Vuejs--父子组件之间的通信__Vue.js
发布于 4 年前 作者 banyungong 1640 次浏览 来自 分享
粉丝福利 : 关注VUE中文社区公众号,回复视频领取粉丝福利

存在数据通信的原因

首先,我们知道,组件有自己的data。他只能访问自己data中的数据,而不能访问其他组件或实例中的数据,但是往往我们需要进行一些数据传递。从父组件传给子组件,比如说最外层的根组件进行了数据请求,但是里面的数据有一些是属于子组件展示的,我们就需要将父组件请求来的数据,传递给子组件。

父组件通过props向子组件传递数据

怎么传递呢?在我看来,就是分为简简单单的两步。

  1. 首先通过自定义属性,向子组件传递数据
    自定义属性的意思是你自己给这个组件元素定义一个它本身不具有的属性并且设置值,那么这个属性以及属性值已经被传递到这个子组件里面了。另外就是自带的属性都有v-bind指令,自定义属性其实也有v-bind指令。正是由于此,导致属性值可以是父组件的数据而不局限于字符串了。所以我们可以把父组件的数据传递给子组件。(换言之,如果没有使用v-bind指令,那么自定义属性全是字符串)
  2. 在子组件中声明父组件传递过来的数据
    仅仅传入组件还不够,如果你想使用这些属性(其实完全可以当变量对待,属性名==变量名,属性值==变量值),那你要给她一个身份,也就是你要对她进行声明,声明的方式是option选项中的props。
    props的形式有两种。

props的形式

例子:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./js/vue.js"></script>
</head>
<body>
    <div id="app">
        <cpn :title="title"></cpn>//通过自定义属性向子组件传递数据
    </div>
    <script>
        const app = new Vue({
            el:"#app",
            data:{
                title:"我是猪"
            },
            components:{
                cpn:{
                    template:`<div><h2>{{title}}</h2></div>`,//直接就可以像data中的数据一样使用传过来的数据
                    props:["title"]//子组件通过props这个option声明这个属性或者说数据,这里使用数组形式
                }
            }
        })
    </script>
</body>
</html>
  1. props是数组
    声明形式:props:["title"]注意这里是字符串的形式,至于为什么是变量名的字符串而不是变量名我也不太清楚是为啥
  2. props是对象
    对象的形式更全面因为它可以做更多的事情:
  • 对属性的传入值进行类型限制 例如:type:Array

  • 给属性设置默认值 例如:default:“abc”

  • 规定属性是否一定要传值 例如:required:bool值

props:{ data1:Number,//data1必须是Number类型 data2:[Number,String]//data2必须是这两者之一 data3:{ type:Number, required:true,//意思是是不是必须传值,true是,false否 default:100//默认值 } }

### 注意事项
- 主要说一下type可以设置的类型:  
1.String2.Number3.Boolean4.Function5.Array6.Object  
- default设置默认值的时候,如果值是一个Object/Array的话,需要通过function的形式,return{}/[]

data4{ default(){ return {} } }

 ## props声明的数据和data的数据之间有什么区别?
### 不同点
1. 来源不同,props的数据由父级组件传进来,data的数据是来自于自己  
2. 正因为props是由父组件传入的,或者说是父组件决定的。如果你再通过子组件改变它的值的话,那么就会特别的混乱。并且解析的时候会抛出警告,并提出建议。就是在data中定义数据并且让props中的属性初始化它。你随便改这些data,因为它是你子组件本身的东西。不会造成刚刚那种混乱的情况。
### 相同点
除了来源不同之外,数据的用法完全一致。能使用的地点完全一致。   


## 单向数据流  
主要是说父组件数据的更改会导致传给子组件数据的变化,反之不会。但是如果传递的是引用类型的数据,比如数组,对象等等。这是会一同变化的。  
举个例子:`<div id="ele"><component :mytext="text">	</component>	</div>`  
如果父组件的text数据改变,则会导致子组件的mytext数据也改变,这也是动态的prop。但是如果mytext在子组件里面做了改变。是不会引起父组件text数据的改变的。  
props的数据传进去,可以把他作为初始值赋给子组件的data内的属性。然后我们后续只用操作这个data数据即可。  
举个例子:
<component :mytext="text"> </component>
<script> new Vue( { components:{ "component",{ props:["mytext"], template:"", data:function() { return{ count:this.mytext//这里,往后我们操作count数据就可 } } } } } ) </script> ``` ## props中的驼峰标识 其实这是一个很简单的问题,HTML有一个很明显的特征就是它对大小写不敏感。浏览器解析HTML代码的时候会把所有的大写字母转为小写。那么当我们在props中声明一个包含大写字母的属性的时候比如 `props:["myTitle"]`,然后父组件再向子组件传入这个数据 `<cpn :myTitle="title"></cpn>`,这样是不会生效的。 ### 原因 是因为`<cpn :myTitle="title"></cpn>`,你传给的自定义属性其实是mytitle,而不是myTitle.所以在props中应该声明mytitle,而不是myTitle. 而变量名一般在命名的时候都有大写字母。也就是我们非要使用myTitle的形式,那么在父传子的时候应该怎么处理呢? 使用驼峰标识:`<cpn :my-title="title"></cpn>`,个人觉得可以理解成转义,**就是变量中如果有大写字母全部转成`-+小写字母`的形式。** # 子组件向父组件传递信息 子组件向父组件传递信息通常是使用自定义事件的方式。 example: ``` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="./js/vue.js"></script> </head> <body>

{{total}}

<cpn :total="total" @change-total="changetotal"></cpn> //父组件监听该事件。
<template id="cpn">
<button @click="decrement">-</button> <button @click="increment">+</button>
</template>
<script>
    const app = new Vue({
        el:"#app",
        data:{
            total:0
        },
       methods:{
         changetotal(counter) {
            this.total=counter;
         } 
       },
        components:{
            cpn:{
                template:"#cpn",
                props:["total"],
                data(){
                    return {
                        counter:this.total
                    }
                },
                methods:{
                    decrement(){
                        this.$emit("change-total",--this.counter);//子组件发射自定义事件
                    },
                    increment()
                    {
                        this.$emit("change-total",++this.counter);//子组件发射自定义事件
                    }
                }
            }
        }
    })
</script>
</body> </html> ``` ![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1d8825c5f9d94833a1d41ef512bc9bb1~tplv-k3u1fbpfcp-zoom-1.image) - 第一步,子组件发射自定义事件 子组件通过自带的方法:`component.$emit()`发射自定义事件。参数一是自定义事件名,参数二是我们要传递出去的数据。`component.$emit('eventName',data)`. - 第二步,父组件监听该事件。 父组件通过监听它,从而做出相应操作,就像监听传统事件一样。 `<cpn :total="total" @change-total="changetotal"></cpn> `这里就是监听子组件的changetotal事件,当他触发(发射)的时候,就会自行自身的changetotal函数。 **那么传递的数据是怎么传递给父组件的呢?** 发射自定义事件的时候,第二个参数是要传递的数据,说明它发射出来了。那父组件怎么接收的呢?当他执行相应操作的时候,也就是执行changetotal函数的时候,他就把发射出来的数据默认赋值给了这个函数的第一个参数。 ``` methods:{ changetotal(counter) {//这个counter已经默认接收了发出的counter this.total=counter; } }, ``` 前文我们说过当监听传统事件的时候,要触发的函数如果有一个形参,但是我们没有给它传递参数,并且没有小括号例如这里的changetotal:`<cpn :total="total" @change-total="changetotal"></cpn> `那么实际上,会给形参传递的是浏览器事件对象`$event`。但是监听自定义事件的时候,他并不会产生这个`$event`对象,**它默认传递的并不是这个事件对象,而是你发射出来的数据。**即这里的counter!=$event,而是==子组件的counter. # 总结 - 父组件向子组件传递数据使用自定义属性。 - 子组件向父组件传递信息使用自定义事件。

版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: 羅藝 原文链接:https://juejin.im/post/6867198126113947662

回到顶部