• vue学习笔记(八)组件校验&通信


    前言

    在上一章博客的内容中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中的组件通信,毕竟这个才是重点,现在的组件通信也才是入门。欢迎大家在博客下方评论,我们一起交流。

  • 相关阅读:
    盒模型(框模型)
    边框
    尺寸及溢出处理
    HTML标签分类
    尺寸单位和颜色的取值
    选择器的优先级
    C++ 代码模板
    LC 425. Word Squares 【lock,hard】
    LC 660. Remove 9 【lock, hard】
    LC 759. Employee Free Time 【lock, hard】
  • 原文地址:https://www.cnblogs.com/jjgw/p/11876826.html
Copyright © 2020-2023  润新知