前言

在上一章博客的内容中vue学习笔记(七)组件我们初步的认识了组件,并学会了如何定义局部组件和全局组件,上一篇内容仅仅只是对组件一个简单的入门,并没有深入的了解组件当中的其它机制,本篇博客将会带大家深入了解组件的其它知识,组件的校验,组件的通信等等。

本章目标

  • 学会组件简单的校验
  • 学会父组件向子组件传递数据
  • 学会子组件向父组件传递数据

父组件向子组件传递数据

父组件向子组件传递数据实现的方式特别简单,只用使用props进行数据传递就可以了。

语法:props[‘属性1’,‘属性2’,…]

我找了一张图给大家参考一下

在 Vue.js 中,父子组件的关系可以总结为 props down, events up 。父组件通过 props 向下传递数据给子组件,子组件通过 events 给父组件发送消息

(1)简单的父组件向子组件传递信息

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>父组件向子组件传递信息</title>
    </head>
    <body>
        <div id="app">
            <my-content :title='title' :content='content'></my-content>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    title:'标题',
                    content:'内容'
                },
                methods:{
                    
                },
                computed:{
                    
                },
                components:{
                    'myContent':{
                        props:['title','content'],
                        template:`<div>
                            <h1>{{title}}</h1>
                            <p>{{content}}</p>
                        </div>`
                    }
                }
                
            })
            
        </script>
    </body>
</html>

结果:显示标题和内容,最简单的父组件向子组件传递信息我们就实现了,但是里面还有其它的小知识点我们没有讲解到,而且后期开发都是使用vue-cli来实现父组件向子组件传递数据的,所以这个知识点下一篇博客会讲解到,不会搭建vue-cli项目的朋友可以参考这篇博客使用webstorm搭建vue-cli项目后续的许多文章都会使用vue-cli中的组件进行讲解,而不是通过简单的引入vue.js文件了,所以强烈推荐大家一定要学会搭建vue-cli项目。

总结:父组件向子组件传递数据使用props

(2)props传递整个对象

假设父组件中的对象含有多个属性,我们每一个属性都需要进行传递,那么是不是需要绑定每一个属性呢?

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>props传递多个属性</title>
    </head>
    <body>
        <div id="app">
            <my-content :title="attr.title" :content1="attr.content1" :content2="attr.content2"></my-content>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    attr:{
                        title:'新闻主题',
                        content1:'新闻内容1',
                        content2:'新闻内容2',
                    }
                },
                methods:{
                    
                },
                computed:{
                    
                },
                components:{
                    'myContent':{
                        props:['title','content1','content2'],
                        template:'<div><h4>{{title}}</h4><span>{{content1}}</span><span>{{content2}}</span></div>'
                    }
                }
                
            })
            
        </script>
    </body>
</html>

现在这个实例所表现出来的就是一个对象里面有很多个属性,现在仅仅只有三个属性而已,我们的父组件绑定属性的时候就需要绑定三个属性,如果是100个或者1000个那么绑定组件的那个标签不是很长吗?和同事一起开发的话,你的同事看到那么长的代码肯定会怀疑人生的,所以为了解决这个问题我们将代码改写下面那样。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>props传递多个属性</title>
    </head>
    <body>
        <div id="app">
            <my-content  v-bind="attr"></my-content>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    attr:{
                        title:'新闻主题',
                        content1:'新闻内容1',
                        content2:'新闻内容2',
                    }
                },
                methods:{
                    
                },
                computed:{
                    
                },
                components:{
                    'myContent':{
                        props:['title','content1','content2'],
                        template:'<div><h4>{{title}}</h4><span>{{content1}}</span><span>{{content2}}</span></div>'
                    }
                }
                
            })
            
        </script>
    </body>
</html>

在这里我们使用v-bind将整个对象打包传递过去,这样一来就大大的减少了代码的冗余度了,那么你的同事肯定夸你小伙子不错,你就等着被领导表扬吧!(自己脑补出来的)

data必须是函数

为什么说data必须是一个函数呢?这个知识点在上一篇博客中没有提及到,现在的话我们来讨论一下,这个案例就足够说明了。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>data必须是函数</title>
    </head>
    <body>
        <div id="app">
            <my-content></my-content>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            Vue.component('myContent',{
                data:{
                    msg:'hello world'
                },
                template:'<span>{{msg}}</span>',
            });
            let vm=new Vue({
                el:'#app'
            })
        </script>
    </body>
</html>

结果:

 控制台显示vue.js给出的警告也是提出data必须是一个函数,好的既然规定data必须是一个函数,那我们就按照它说的来做。看看结果如何

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>data必须是函数</title>
    </head>
    <body>
        <div id="app">
            <my-content></my-content>
            <my-content></my-content>
            <my-content></my-content>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            var data={
                count:1
            }
            Vue.component('myContent',{
                data:function(){
                    return data;
                },
                template:'<button @click="count+=1">{{count}}</button>',
            });
            let vm=new Vue({
                el:'#app'
            })
        </script>
    </body>
</html>

 

现在的话,我们的data是一个函数,但是解决一个问题的同时新的问题又出现了,点击任意一个按钮的时候发现其它按钮的值都会发生改变,这是因为我们引用了同一个对象,我们知道对象是引用传递的,所以为了改变这一种情况,我们让每个组件内部都有自己的状态而不会去干涉其它组件。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>data必须是函数</title>
    </head>
    <body>
        <div id="app">
            <my-content></my-content>
            <my-content></my-content>
            <my-content></my-content>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            Vue.component('myContent',{
                data(){
                    return{
                        count:1
                    }
                },
                template:'<button @click="count+=1">{{count}}</button>',
            });
            let vm=new Vue({
                el:'#app'
            })
        </script>
    </body>
</html>

结果:

 我们在data中使用return返回新的对象,这样一来每一个组件都有自己本身的状态了,从而不会去影响其它的组件。

组件的校验

组件的校验从字面上看就是对父组件向子组件传递的信息中,子组件对父组件传递过来的信息进行验证,一方面是为了数据的安全性,另一方面给他人使用的时候也可以限制他人传递过来的数据的验证。

Vue.component('example', {
  props: {
    // 基础类型检测 (`null` 意思是任何类型都可以)
    propA: Number,
    // 多种类型
    propB: [String, Number],
    // 必传且是字符串
    propC: {
      type: String,
      required: true
    },
    // 数字,有默认值
    propD: {
      type: Number,
      default: 100
    },
    // 数组/对象的默认值应当由一个工厂函数返回
    propE: {
      type: Object,
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator: function (value) {
        return value > 10
      }
    }
  }
})

type可选:

  • Sting
  • Number
  • Boolean
  • Function
  • Object
  • Array

(1)示例一

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>组件的校验示例一</title>
    </head>
    <body>
        <div id="app">
            <my-content :name="name" :age="age" :sex="sex"></my-content>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    name:'小明',
                    age:18,
                    sex:''
                },
                methods:{
                    
                },
                computed:{
                    
                },
                components:{
                    'myContent':{
                        props:{
                            name:{
                                type:Number,
                                required:true,
                            },
                            age:{
                                type:Number,
                                default:20,
                            },
                            sex:{
                                type:String,
                                default:''
                            }
                        },
                        template:'<div><span>{{name}}</span><span>{{age}}</span><span>{{sex}}</span></div>'
                    }
                }
                
            })
            
        </script>
    </body>
</html>

结果:

我们对name做了验证规定,name的类型必须是Number,而传递过来的却是String,所以vue.js给出了警告,对name属性也要求是必须传递的参数,required:true,对sex给定默认值,当我们没有传递sex的时候,子组件中的sex默认值是女的,所以有了组件的这一校验,大大提高的数据的安全性。

(2)示例二

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>组件的校验示例二</title>
    </head>
    <body>
        <div id="app">
            <my-content name="喜马拉雅" :size="8848" :is-boy="false" address="中国西藏"></my-content>
        </div>
        <script type="text/template" id="template1">
            <div>姓名:{{name}}身高:{{size}}是否是男生:{{isBoy}}位置:{{address}}重量:{{weight.ton}}亿吨</div>
        </script>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            Vue.component('myContent',{
                template:'#template1',
                props:{
                    name:{
                        type:String,    //类型
                        required:true,    //规定必填
                    },
                    size:Number,    //规定整形
                    isBoy:Boolean,    //规定布尔值
                    age:[Number,String],//    多种类型
                    address:{
                        type:String,
                        default:'中国',
                        validator:function(value){
                            return value.indexOf('中国')>=0
                        }
                    },
                    weight:{
                        type:Object,
                        default:function(){
                            return {ton:999999999}
                        }
                    },
                }
                
            })
            let vm=new Vue({
                el:'#app',
                data:{
                    
                },
                methods:{
                    
                },
                computed:{
                    
                }
                
            })
            
        </script>
    </body>
</html>

结果:

子组件向父组件传递数据

我们知道,父组件是使用 props 传递数据给子组件,但如果子组件要把数据传递回去,应该怎样做?那就是自定义事件!每个 Vue 实例都实现了事件接口(Events interface),即:

  • 使用 $on(eventName) 监听事件
  • 使用 $emit(eventName) 触发事件

接下来我们一步一步对子组件向父组件传递数据进行讲解,先来看下一个特别简单的示例,这个需求是这样的,当我们改变子组件的count的时候,父组件的count也需要改变

(1)子组件实现功能

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>子组件向父组件传递数据</title>
    </head>
    <body>
        <div id="app">
            <my-content :count="count"></my-content>
            父组件的count:<span>{{count}}</span>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    count:1
                },
                methods:{
                    
                },
                computed:{
                    
                },
                components:{
                    'myContent':{
                        data(){
                            return{
                                ownCount:this.count
                            }
                        },
                        props:['count'],
                        template:'<div>子组件的count:<span>{{ownCount}}</span><button @click="handleClick">add</button></div>',
                        methods:{
                            handleClick(){
                                this.ownCount++;
                            }
                        }
                    }
                }
                
            })
            
        </script>
    </body>
</html>

我们通过父组件向子组件传递了count值,然后子组件中的count值在点击之后会发生改变,但是这仅仅实现了子组件的值改变,并没有实现父组件中的count改变

(2)向父组件传递信息

需要向父组件传递信息的话,我们就需要子组件通知父组件,然后父组件在做相应的处理,而需要通知父组件我们就i需要用到this.$emit(‘事件名称’,值)。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>子组件向父组件传递数据</title>
    </head>
    <body>
        <div id="app">
            <my-content :count="count" @add="handleAdd"></my-content>
            父组件的count:<span>{{count}}</span>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    count:1
                },
                methods:{
                    handleAdd(count){
                        this.count=count;
                    }
                },
                computed:{
                    
                },
                components:{
                    'myContent':{
                        data(){
                            return{
                                ownCount:this.count
                            }
                        },
                        props:['count'],
                        template:'<div>子组件的count:<span>{{ownCount}}</span><button @click="handleClick">add</button></div>',
                        methods:{
                            handleClick(){
                                this.ownCount+=10;
                                this.$emit('add',this.ownCount);
                            }
                        }
                    }
                }
                
            })
            
        </script>
    </body>
</html>

结果:

我们在子组件点击按钮的时候添加了this.$emit()来通知父组件,然后将需要注册的事件和每次改变的值传递了过去,这时父组件注册子组件传递过来的事件和值,然后将父组件中的值替换子组件传递过来的就可以了。

(3)优化子组件向父组件传递信息

其实我们可以做到让父组件来控制子组件中count值的变化,假设我们每次让子组件增加10,我们先来一个小案例

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>子组件向父组件传递数据</title>
    </head>
    <body>
        <div id="app">
            <my-content :count="count" @add="handleAdd" v-bind="info"></my-content>
            父组件的count:<span>{{count}}</span>
            <button @click="handleClick">点击</button>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    count:1,
                    info:{
                        name:'',
                        age:18
                    }
                },
                methods:{
                    handleAdd(count){
                        this.count=count;
                    },
                    handleClick(){
                        this.info.name=''
                    }
                },
                computed:{
                    
                },
                components:{
                    'myContent':{
                        data(){
                            return{
                                ownCount:this.count
                            }
                        },
                        props:['count','name','age'],
                        template:'<div><span>姓名:{{name}}</span><br/>子组件的count:<span>{{ownCount}}</span><button @click="handleClick">add</button></div>',
                        methods:{
                            handleClick(){
                                this.ownCount+=10;
                                this.$emit('add',this.ownCount);
                            }
                        }
                    }
                }
                
            })
            
        </script>
    </body>
</html>

结果:

 

当我们点击按钮的时候发现父组件传递给子组件的姓名发生了改变,那么我们只要改变父组件中的count的值,那么子组件中count的值也会发生改变

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>子组件向父组件传递数据</title>
    </head>
    <body>
        <div id="app">
            <my-content :count="count" @add="hanldeAdd"></my-content>
            父组件中的count<span>{{count}}</span>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    count:1
                },
                methods:{
                    hanldeAdd(count){
                        this.count+=count;
                    }
                },
                computed:{
                    
                },
                components:{
                    'myContent':{
                        data(){
                            return{
                                //ownCount=this.count;
                            }
                        },
                        props:['count'],
                        template:'<div>子组件中的count<span>{{count}}</span><button @click="handleClick">add</button></div>',
                        methods:{
                            handleClick(){
                                this.$emit('add',10)
                            }
                        }
                    }
                }
                
            })
            
        </script>
    </body>
</html>

这样一来父子组件的简单通信就全部讲完了,接下来为了巩固一下父子组件之前通信的知识,我们来做一个任务清单

总结:子组件向父组件传递信息this.$emit(‘事件名称’,值)

(4)任务清单

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>任务清单</title>
    </head>
    <body>
        <div id="app">
            任务:<input type="text" v-model="newTask" @keyup.enter="addNew" placeholder="请输入您要完成的任务" />
            <ul>
                <li is="todoImte" v-for="(item,index) of tasks" :title="item" @remove="removeItem(index)"></li>
            </ul>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            Vue.component('todoImte',{
                props:['title'],
                template:"<li>{{title}}<button @click='$emit(\"remove\")'>X</button></li>"
            });
            let vm=new Vue({
                el:'#app',
                data:{
                    newTask:'',
                    tasks:['买一本书','写一次博客','和朋友一起吃饭']
                },
                methods:{
                    addNew(){
                        this.tasks.unshift(this.newTask);
                        this.newTask='';
                    },
                    removeItem(index){
                        if(confirm('确定要删除吗?')){
                            this.tasks.splice(index);
                        }
                    }
                },
                computed:{
                    
                }
                
            })
            
        </script>
    </body>
</html>

结果:

 

 

任务清单中,首先有个添加任务的框,然后通过addNew方法将任务添加到任务清单中,子组件中通过this.$emit()告诉父组件执行相应的方法,任务清单中,我们可以添加任务清单也可以删除任务清单,添加任务清单的话,没有涉及组件通信,二删除任务清单的时候,子组件通知父组件需要删除那个任务,然后将需要删除任务的索引传递过去,父组件根据传递过来的index进行删除。

总结

本篇博客主要讲了三个知识点,组件的校验,父组件向子组件传递信息(通过props),子组件向父组件传递信息(通过this.$emit),本篇博客讲解的内容也比较简单,但是我认为这仅仅也还是组件的开始,下一遍博客我们将会讲解vue-cli中的组件通信,毕竟这个才是重点,现在的组件通信也才是入门。欢迎大家在博客下方评论,我们一起交流。

版权声明:本文为jjgw原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/jjgw/p/11876826.html