第5章:组件即组件间的通信

  一,组件component

  1.什么是组件?

  组件(Component)是Vue.js最强大的功能之一。组件可以扩展HTML元素,封装可重用的代码。

  组件是自定义元素(对象)

  2.定义组件的方式

  方式1:先创建组件构造器,然后由组件构造器创建组件

  方法2:直接创建组件

  示例day03/01.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>定义组件的两种方法</title>
    <script src="js/vue.js"></script>
</head>
<body>
    <div id="itany">
        <hello></hello>
        <world></world>
    </div>
    <script>
        // 方式1:先创建组件构造器,然后由组件构造器创建组件
        // 1.使用Vue.extend()创建一个组件构造器
        var MyComponent=Vue.extend({
            template:'<h3>Hello World!</h3>'
        });
        // 2.使用Vue.component(标签名,组件构造器),根据组件构造器来创建组件
        Vue.component('hello',MyComponent)

        // 方式2:直接创建组件,简洁一些
        Vue.component('world',{
            template:'<h1>你好,世界</h1>'
        });
        new Vue({
            el:'#itany',
        });
    </script>
</body>
</html>

  页面显示

  定义组件以后再页面引用组件相当于把定义的template的内容引用入页面

 

   3.组件的分类

  分类:全局组件,局部组件

  注意:组件不能直接引用vue示例的数据,例如vue实例定义了name数据,在定义组件中直接引用

  day03/02.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>定义组件的两种方法</title>
    <script src="js/vue.js"></script>
</head>
<body>
    <div id="itany">
        <my-hello></my-hello>
        <my-world></my-world>
    </div>
    <script>
        // 全局组件,可以在所有vue实例中使用
        Vue.component('my-hello',{
            template:'<h3>{{name}}</h3>',  // 组件中不能直接引用vue实例的数据,会报name没有定义的错
            // data(){  // 在组件中存储数据时,必须以函数形式,函数返回一个对象
            //     return {
            //         name:'alice'
            //     }
            // }
        })
        new Vue({
            el:'#itany',
            data:{
                msg:'网博'
            },
            // 局部组件,只能在当前vue实例中使用
            components:{
                'my-world':{
                    template:'<h3>{{age}}</h3>',
                    data(){
                        return{
                            age:25
                        }
                    }
                }
            }
        });
    </script>
</body>
</html>

  访问页面报错,这个属性是没有定义的

 

   如果需要在组件中插入数据,不能直接像vue示例那样定义而必须以函数返回的方式返回一个对象

  下面代码演示全局组件和局部组件以及数据的定义方式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>定义组件的两种方法</title>
    <script src="js/vue.js"></script>
</head>
<body>
    <div id="itany">
        <my-hello></my-hello>
        <my-world></my-world>
    </div>
    <script>
        // 全局组件,可以在所有vue实例中使用
        Vue.component('my-hello',{
            template:'<h3>{{name}}</h3>',  // 组件中不能直接引用vue实例的数据,会报name没有定义的错
            data(){  // 在组件中存储数据时,必须以函数形式,函数返回一个对象
                return {
                    name:'alice'
                }
            }
        })
        new Vue({
            el:'#itany',
            data:{
                msg:'网博'
            },
            // 局部组件,只能在当前vue实例中使用
            components:{
                'my-world':{
                    template:'<h3>{{age}}</h3>',
                    data(){
                        return{
                            age:25
                        }
                    }
                }
            }
        });
    </script>
</body>
</html>

  页面显示

  两个数据都是定义函数返回的形式

 

   4.引用模板

  在定义组件的时候如果需要在template中拼接比较多的数据则会比较麻烦而且代码不易读取,可以使用模板的方式引入

  模板定义使用<template></template>标签,注意模板内部必须有且只有一个根元素

  示例day03/03.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>引用模板</title>
    <script src="js/vue.js"></script>
</head>
<body>
    <div id="itany">
        <my-hello></my-hello>
    </div>
    <template id='wbs'>
        <!-- template标签内部必须有且只有一个根元素 -->
        <div>
            <h3>{{msg}}</h3>
            <ul>
                <li v-for="value in arr">{{value}}</li>
            </ul>
        </div>
    </template>
    <script>
   
        new Vue({
            el:'#itany',
            data:{
                msg:'tom'
            },
            // 局部组件,只能在当前vue实例中使用
            components:{
                'my-hello':{
                    // 使用id引用模板
                    template:'#wbs',
                    data(){
                        return{
                            msg:'欢迎来到南京网博',
                            arr:['tom','jack','mike']
                        }
                    }
                }    
            }
        });
    </script>
</body>
</html>

  解析

 

 

   页面显示

 

 

   引用模板就是将组件的内容放到<template>中并引用

  5.动态组件

  多个组件使用同一个挂载点,然后动态的在它们之间切换

  <component :is="">组件 

  示例04.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>引用模板</title>
    <script src="js/vue.js"></script>
</head>
<body>
    <div id="itany">
        <button  @click="flag='my-hello'">显示hello组件</button>
        <button @click="flag='my-world'">显示world组件</button>
        <div>
           <component :is="flag">

           </component>
        </div>
    </div>
   
    <script>
   
        new Vue({
            el:'#itany',
            data:{
                flag:'my-hello'
            },
            // 局部组件,只能在当前vue实例中使用
            components:{
                'my-hello':{
                    // 使用id引用模板
                    template:'<h3>我是hello组件</h3>',  // 指定组件的名称默认为标签名,可以不设置
                    data(){
                        return{
                            x:Math.random()
                        }
                    }
                },
                'my-world':{
                    // 使用id引用模板
                    template:'<h3>我是world组件</h3>',  // 指定组件的名称默认为标签名,可以不设置
                    data(){
                        return{
                            y:Math.random()
                        }
                    }
                },    
            }
        });
    </script>
</body>
</html>

  解析:使用标签<component :is="flag">根据:is值来显示对应的组件,开始flag值为my-hello所以显示组件my-hello,点击切换用户flag的值改变了就显示对应的组件

  页面显示

 

 

 

 

 

   <keep-alive>组件,包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们

  keep-live组件缓存非活动组件,可以保留状态,避免重新渲染,默认是销毁并重新渲染

  示例,在组件内有两个随机生成的数x,y下面显示这两个随机数,没有使用<keep-alive>组件的时候

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>引用模板</title>
    <script src="js/vue.js"></script>
</head>
<body>
    <div id="itany">
        <button  @click="flag='my-hello'">显示hello组件</button>
        <button @click="flag='my-world'">显示world组件</button>
        <div>
           <component :is="flag">

           </component>
        </div>
    </div>
   
    <script>
   
        new Vue({
            el:'#itany',
            data:{
                flag:'my-hello'
            },
            // 局部组件,只能在当前vue实例中使用
            components:{
                'my-hello':{
                    // 使用id引用模板
                    template:'<h3>我是hello组件,{{x}}</h3>',  // 指定组件的名称默认为标签名,可以不设置
                    data(){
                        return{
                            x:Math.random()
                        }
                    }
                },
                'my-world':{
                    // 使用id引用模板
                    template:'<h3>我是world组件,{{y}}</h3>',  // 指定组件的名称默认为标签名,可以不设置
                    data(){
                        return{
                            y:Math.random()
                        }
                    }
                },    
            }
        });
    </script>
</body>
</html>

  页面显示

 

 

   使用<keep-alive>组件,上面代码修改一处使用<keep-alive>组件

 <keep-alive>
                <component :is="flag">
                </component>
</keep-alive>

  页面显示

 

 

   二,组件间的数据传递

  1.父子组件

  在一个组件内部定义另一个组件,称为父子组件

  子组件只能在父组件内部使用

  示例day03/05.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>父子组件即组件间数据传递</title>
    <script src="js/vue.js"></script>
</head>
<body>
    <div id="itany">
        <my-hello></my-hello>
        <!-- 子组件不能在此处引用 -->
        <my-world></my-world>
    </div>
    <template id="hello">
        <div>
            <h3>我是hello父组件</h3>
           
        </div>
    </template>
    <template id="world">
        <div>
            <h3>我是world子组件</h3>
        </div>
    </template>
    <script>
   
        new Vue({
            el:'#itany',
            data:{
                flag:'my-hello'
            },
            // 局部组件,只能在当前vue实例中使用
            components:{
                'my-hello':{                                       
                    data(){
                        return{
                            msg:'网博',
                            name:'tom',
                            age:23,
                            user:{id:9527,username:'唐伯虎'}
                        }
                    },
                    template:'#hello',
                    components:{
                        'my-world':{
                            template:'#world'
                        }
                    }
                
                },   
            }
        });
    </script>
</body>
</html>

  解析:

  我们定义了一个组件my-hello,然后在这个组件里面定义了一个子组件my-world

 

 

   使用模板引用,页面显示my-hello但是没有显示my-world

 

 

   如果想要引用子组件需要在父组件引用的template里面引用,修改代码在父组件里面引用

<template id="hello">
        <div>
            <h3>我是hello父组件</h3>
            <hr>
            <my-world></my-world>
        </div>
</template>

  页面显示

 

 

   默认情况下,子组件无法访问父组件中的数据,每个组件实例的作用域是独立的

  示例,父组件访问自己的数据是没有问题的,子组件访问父组件数据报错

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>父子组件即组件间数据传递</title>
    <script src="js/vue.js"></script>
</head>
<body>
    <div id="itany">
        <my-hello></my-hello>
        <!-- 子组件不能在此处引用 -->
        <!-- <my-world></my-world> -->
    </div>
    <template id="hello">
        <div>
            <h3>我是hello父组件</h3>
            <h3>访问自己的数据:{{msg}},{{name}},{{age}},{{user.username}}</h3>
            <hr>
            <my-world></my-world>
        </div>
    </template>
    <template id="world">
        <div>
            <h4>我是world子组件</h4>
            <h4>访问父组件的数据:{{msg}},{{name}},{{age}},{{user.username}}</h4>
        </div>
    </template>
    <script>
   
        new Vue({
            el:'#itany',
            data:{
                flag:'my-hello'
            },
            // 局部组件,只能在当前vue实例中使用
            components:{
                'my-hello':{                                       
                    data(){
                        return{
                            msg:'网博',
                            name:'tom',
                            age:23,
                            user:{id:9527,username:'唐伯虎'}
                        }
                    },
                    template:'#hello',
                    components:{
                        'my-world':{
                            template:'#world'
                        }
                    }
                
                },   
            }
        });
    </script>
</body>
</html>

  解析

 

 

   页面显示

 

 

 

  2.组件间数据传递(通信)

  2.1 子组件访问父组件的数据

  a)父组件在调用子组件时,绑定想要获取的父组件中的数据

  b)在子组件内部,使用props选项声明获取的数据,即接受来自父组件的数据

  示例 day03/05.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>父子组件即组件间数据传递</title>
    <script src="js/vue.js"></script>
</head>
<body>
    <div id="itany">
        <my-hello></my-hello>
        <!-- 子组件不能在此处引用 -->
        <!-- <my-world></my-world> -->
    </div>
    <template id="hello">
        <div>
            <h3>我是hello父组件</h3>
            <h3>访问自己的数据:{{msg}},{{name}},{{age}},{{user.username}}</h3>
            <hr>
            <!-- 父组件把数据传递给子组件 -->
            <my-world :message="msg" :name="name" :age="age" :user="user"></my-world>
        </div>
    </template>
    <template id="world">
        <div>
            <h4>我是world子组件</h4>
            <!-- <h4>访问父组件的数据:{{msg}},{{name}},{{age}},{{user.username}}</h4> -->
            <h4>访问父组件的数据:{{message}},{{name}},{{age}},{{user.username}}</h4>
        </div>
    </template>
    <script>
   
        new Vue({
            el:'#itany',
            data:{
                flag:'my-hello'
            },
            // 局部组件,只能在当前vue实例中使用
            components:{
                'my-hello':{                                       
                    data(){
                        return{
                            msg:'网博',
                            name:'tom',
                            age:23,
                            user:{id:9527,username:'唐伯虎'}
                        }
                    },
                    template:'#hello',
                    components:{
                        'my-world':{
                            template:'#world',
                            props:['message','name','age','user']
                        },
                       
                    }
                
                },   
            }
        });
    </script>
</body>
</html>

  解析

 

 

   页面显示

 

 

 

 

 

   注:组件中的数据总共有三种形式:data,props,computed

  以上从父组件传递给子组件的数据是通过一个字符串接收也可以使用对象的方式接收,使用对象方式接收允许配置高级设置,如类型判断,数据校验,设置默认值等

  示例,修改props代码如下

                            // props:['message','name','age','user']  // 简单的字符串组接收
                            // 也可以是对象,允许配置高级设置,如类型判断,数据校验,设置默认值等
                            props:{
                                message:String,  // 必须是字符串
                                name:{
                                    type:String, // 必须是字符串
                                    required:true // 必须传递
                                },
                                age:{
                                    type:Number,  // 必须是数字
                                    default:18,   // 如果没有传递则给一个默认值
                                    validator:function(value){  // 判断数字需要大于0,如果大于0则返回true,否则返回false
                                        return value>=0
                                    }
                                },
                                user:{
                                    type:Object,  // 对象或数组的默认值必须使用函数的形式来返回
                                    default:function(){
                                        return {id:3306,username:'秋香'}
                                    }              
                                }
                            }

  如果传递的数据没有满足定义的需求则在控制台报错,例如把age修改为-23

 

 

   如果接收的数据是一个对象,则数据需要以函数的形式返回例如上例中的users

 

 

   简单直接写字符串就可以了,如果想要做规则的校验则使用下面这中方法

  type可以是下列原生构造函数中的一个

String
Number
Boolean
Array
Object
Date
Function
Symbol

  

  2.2父组件访问子组件的数据

  父组件访问子组件的数据可以吗,首先直接写

  05.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>父子组件即组件间数据传递</title>
    <script src="js/vue.js"></script>
</head>
<body>
    <div id="itany">
        <my-hello></my-hello>
        <!-- 子组件不能在此处引用 -->
        <!-- <my-world></my-world> -->
    </div>
    <template id="hello">
        <div>
            <h3>我是hello父组件</h3>
            <h3>访问自己的数据:{{msg}},{{name}},{{age}},{{user.username}}</h3>
            <h3>访问子组件的数据:{{sex}},{{height}}</h3>
            <hr>
            <!-- 父组件把数据传递给子组件 -->
            <my-world :message="msg" :name="name" :age="age" :user="user"></my-world>
            
        </div>
    </template>
    <template id="world">
        <div>
            <h4>我是world子组件</h4>
            <!-- <h4>访问父组件的数据:{{msg}},{{name}},{{age}},{{user.username}}</h4> -->
            <h4>访问父组件的数据:{{message}},{{name}},{{age}},{{user.username}}</h4>
            <h4>访问子组件自己的数据:{{sex}},{{height}}</h4>
        </div>
    </template>
    <script>
   
        new Vue({
            el:'#itany',
            data:{
                flag:'my-hello'
            },
            // 局部组件,只能在当前vue实例中使用
            components:{
                'my-hello':{                                       
                    data(){
                        return{
                            msg:'网博',
                            name:'tom',
                            age:23,
                            user:{id:9527,username:'唐伯虎'}
                        }
                    },
                    template:'#hello',
                    components:{
                        'my-world':{
                            data(){
                                return {
                                    sex:'male',
                                    height:180.5
                                }
                            },
                            template:'#world',
                            // props:['message','name','age','user']  // 简单的字符串组接收
                            // 也可以是对象,允许配置高级设置,如类型判断,数据校验,设置默认值等
                            props:{
                                message:String,  // 必须是字符串
                                name:{
                                    type:String, // 必须是字符串
                                    required:true // 必须传递
                                },
                                age:{
                                    type:Number,  // 必须是数字
                                    default:18,   // 如果没有传递则给一个默认值
                                    validator:function(value){  // 判断数字需要大于0,如果大于0则返回true,否则返回false
                                        return value>=0
                                    }
                                },
                                user:{
                                    type:Object,  // 对象或数组的默认值必须使用函数的形式来返回
                                    default:function(){
                                        return {id:3306,username:'秋香'}
                                    }              
                                }
                            }
                        },
                       
                    }
                
                },   
            }
        });
    </script>
</body>
</html>

  解析:我们往子组件添加了一些数据,在子组件内部是可以直接访问的,在父组件则无法访问,控制台报错是未定义

  页面显示

 

 

   a)在子组件中使用vm.$emit(事件名,数据)触发一个自定义事件,发送数据

  b)父组件在使用子组件的地方监听子组件触发的事件,并在父组件定义方法获取数据

  小结:子组件通过event是给父组件发送消息,实际上就是子组件把自己的数据发送给父组件

  示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>父子组件即组件间数据传递</title>
    <script src="js/vue.js"></script>
</head>
<body>
    <div id="itany">
        <my-hello></my-hello>
        <!-- 子组件不能在此处引用 -->
        <!-- <my-world></my-world> -->
    </div>
    <template id="hello">
        <div>
            <h3>我是hello父组件</h3>
            <h3>访问自己的数据:{{msg}},{{name}},{{age}},{{user.username}}</h3>
            <h3>访问子组件的数据:{{sex}},{{height}}</h3>
            <hr>
            <!-- 父组件把数据传递给子组件 -->
            <my-world :message="msg" :name="name" :age="age" :user="user" @e-world="getData"></my-world>
            
        </div>
    </template>
    <template id="world">
        <div>
            <h4>我是world子组件</h4>
            <!-- <h4>访问父组件的数据:{{msg}},{{name}},{{age}},{{user.username}}</h4> -->
            <h4>访问父组件的数据:{{message}},{{name}},{{age}},{{user.username}}</h4>
            <h4>访问子组件自己的数据:{{sex}},{{height}}</h4>
            <button @click="send">将子组件的数据向上发送给父组件</button>
        </div>
    </template>
    <script>
   
        new Vue({
            el:'#itany',
            data:{
                flag:'my-hello'
            },
            // 局部组件,只能在当前vue实例中使用
            components:{
                'my-hello':{     
                    methods:{
                        getData(sex,height){
                            this.sex=sex,
                            this.height=height
                        }
                    },

                    data(){
                        return{
                            msg:'网博',
                            name:'tom',
                            age:23,
                            user:{id:9527,username:'唐伯虎'},
                            sex:'',
                            height:0
                        }
                    },
                    template:'#hello',
                    components:{
                        'my-world':{
                            data(){
                                return {
                                    sex:'male',
                                    height:180.5
                                }
                            },
                            template:'#world',
                            // props:['message','name','age','user']  // 简单的字符串组接收
                            // 也可以是对象,允许配置高级设置,如类型判断,数据校验,设置默认值等
                            props:{
                                message:String,  // 必须是字符串
                                name:{
                                    type:String, // 必须是字符串
                                    required:true // 必须传递
                                },
                                age:{
                                    type:Number,  // 必须是数字
                                    default:18,   // 如果没有传递则给一个默认值
                                    validator:function(value){  // 判断数字需要大于0,如果大于0则返回true,否则返回false
                                        return value>=0
                                    }
                                },
                                user:{
                                    type:Object,  // 对象或数组的默认值必须使用函数的形式来返回
                                    default:function(){
                                        return {id:3306,username:'秋香'}
                                    }              
                                }
                            },
                            methods:{
                                send(){
                                    // console.log(this)  // 此处的this是当前子组件实例my-world
                                    this.$emit('e-world',this.sex,this.height); // 使用emit触发事件,发送数据
                                }
                            }
                        },
                       
                    }
                
                },   
            }
        });
    </script>
</body>
</html>

  解析

  点击button调用send方法向上发数据

 <button @click="send">将子组件的数据向上发送给父组件</button>

  在子组件内定义send方法,事件名称e-world是自定义的,父组件通过这个定义的事件名来换取子组件发送的数据,后面的参数是子组件的数据,可以是多个

                            methods:{
                                send(){
                                    // console.log(this)  // 此处的this是当前子组件实例my-world
                                    this.$emit('e-world',this.sex,this.height); // 使用emit触发事件,发送数据
                                }
                            }
                        },

  父组件接收数据

<my-world :message="msg" :name="name" :age="age" :user="user" @e-world="getData"></my-world>

  其中

@e-world="getData"  # 监听事件e-world如果事件有发送数据则调用getData方法获取数据,这个方法是在父组件中定义的

  在父组件中定义getData方法获取子组件发送的数据

                    methods:{
                        getData(sex,height){
                            this.sex=sex,
                            this.height=height
                        }
                    },

                    data(){
                        return{
                            msg:'网博',
                            name:'tom',
                            age:23,
                            user:{id:9527,username:'唐伯虎'},
                            sex:'',
                            height:0
                        }
                    },

  注意:需要在父组件的数据data中初始化接收的数据,例如本次接收数据sex和height则在父组件的data中定义好,初始值可以设置为空

  页面显示

 

 

 

 

 

   3.单向数据流

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

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

  示例:为了简单一点,定义一个子组件my-hello,它的父组件就是vue示例本身,把父组件的数据通过props传递给子组件

  day03/06.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>单向数据流</title>
    <script src="js/vue.js"></script>
</head>
<body>
    <div id="itany">
        <h2>父组件:{{name}}</h2>
        <input v-model="name">
        <hr>
        <my-hello :name="name"></my-hello>
    </div>
    <template id="hello">
        <div>  
            <h3>子组件:{{name}}</h3>     
            <!-- <button @click="change">修改数据</button> -->
        </div>
    </template>

    <script>  
       var vm=new Vue({
            el:'#itany', 
            data:{
                name:'tom'
            },
            components:{
                'my-hello':{     
                    template:'#hello',
                    props:['name'],
                    // methods:{
                    //     change(){
                    //         this.name='alice';
                    //     }
                    // }
                } 
            }
        });
    </script>
</body>
</html>

  页面显示

 

   修改代码增加一个按钮修改子组件的数据

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>单向数据流</title>
    <script src="js/vue.js"></script>
</head>
<body>
    <div id="itany">
        <h2>父组件:{{name}}</h2>
        <input v-model="name">
        <hr>
        <my-hello :name="name"></my-hello>
    </div>
    <template id="hello">
        <div>  
            <h3>子组件:{{name}}</h3>     
            <button @click="change">修改数据</button>
        </div>
    </template>

    <script>  
       var vm=new Vue({
            el:'#itany', 
            data:{
                name:'tom'
            },
            components:{
                'my-hello':{     
                    template:'#hello',
                    props:['name'],
                    methods:{
                        change(){
                            this.name='alice';
                        }
                    }
                } 
            }
        });
    </script>
</body>
</html>

  页面显示

 

   而且不允许直接修改父组件重点数据,报错

  解决方法:

    方式1:如果子组件向把它作为局部数据来使用,可以将数据存入另外一个变量中再操作,不影响父组件中的数据

    方式2:如果子组件向修改数据并同步更新父组件,两个方法

      a.使用.sync(1.0版本中支持,2.0版本不支持,2.3版本又开始可以支持了) 需要显示调用一个方法并且在方法里面使用关键字update

      b.定义一个计算属性,处理prop值并返回,注意在JavaScript中对象和数组是引用类型,指向同一个地址空间,然后prop是一个对象或数组,在子数组内部改变它会影响父组件的状态

  方式1在子组件定义一个局部数据存储,修改和调用都使用这个局部变量

  06.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>单向数据流</title>
    <script src="js/vue.js"></script>
</head>
<body>
    <div id="itany">
        <h2>父组件:{{name}}</h2>
        <input v-model="name">
        <hr>
        <my-hello :name="name"></my-hello>
    </div>
    <template id="hello">
        <div>  
            <h3>子组件:{{username}}</h3>     
            <button @click="change">修改数据</button>
        </div>
    </template>

    <script>  
       var vm=new Vue({
            el:'#itany', 
            data:{
                name:'tom'
            },
            components:{
                'my-hello':{     
                    template:'#hello',
                    props:['name'],
                    data(){
                        return{
                            username:this.name
                        }
                    },
                    methods:{
                        change(){
                            this.username='alice';
                        }
                    }
                } 
            }
        });
    </script>
</body>
</html>

  解析:定义局部数据变量username用于存储父组件发送的数据name,调用和修改都使用username

  页面显示

 

   子组件修改更新到父组件示例,版本需要2.3.0以上

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>单向数据流</title>
    <script src="js/vue.js"></script>
</head>
<body>
    <div id="itany">
        <h2>父组件:{{name}}</h2>
        <input v-model="name">
        <hr>
        <my-hello :name.sync="name"></my-hello>
    </div>
    <template id="hello">
        <div>  
            <h3>子组件:{{name}}</h3>     
            <button @click="change">修改数据</button>
        </div>
    </template>

    <script>  
       var vm=new Vue({
            el:'#itany', 
            data:{
                name:'tom'
            },
            components:{
                'my-hello':{     
                    template:'#hello',
                    props:['name'],
                    // data(){
                    //     return{
                    //         name:this.name  // 方式1 将数据存入另外一个变量修改
                    //     }
                    // },
                    methods:{
                        change(){
                            // this.name='alice';
                            this.$emit('update:name','alice')
                        }
                    }
                } 
            }
        });
    </script>
</body>
</html>

  解析

 

   页面显示

 

   使用对象的方式修改子组件可以同步到父组件示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>单向数据流</title>
    <script src="js/vue.js"></script>
</head>
<body>
    <div id="itany">
        <h2>父组件:{{name}}</h2>
        <input v-model="name">
        <h2>父组件:{{user.age}}</h2>
        <hr>
        <my-hello :name.sync="name" :user="user"></my-hello>
    </div>
    <template id="hello">
        <div>  
            <h3>子组件:{{name}}</h3>     
            <h3>子组件:{{user.age}}</h3>     
            <button @click="change">修改数据</button>
        </div>
    </template>

    <script>  
       var vm=new Vue({
            el:'#itany', 
            data:{
                name:'tom',
                user:{
                    name:'zhangsan',
                    age:24
                }
            },
            components:{
                'my-hello':{     
                    template:'#hello',
                    props:['name','user'],
                    // data(){
                    //     return{
                    //         name:this.name  // 方式1 将数据存入另外一个变量修改
                    //     }
                    // },
                    methods:{
                        change(){
                            // this.name='alice';
                            // this.$emit('update:name','alice')
                            this.user.age=12
                        }
                    }
                } 
            }
        });
    </script>
</body>
</html>

  解析:从父组件传递给子组件的是一个对象user,在组件和父组件中他们指向是同一个内存空间,只是修改了这个对象的其中一个属性,并没有修改这个对象本身,所以可以同步

  这种方法是推荐的做法

  页面显示

 

   4.非父子组件间的通信

  非父子组件间的通信,可以通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件

  示例day03/07.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>非父子组件间的通信</title>
    <script src="js/vue.js"></script>
</head>
<body>
    <div id="itany">
        <my-a></my-a>
        <my-b></my-b>
        <my-c></my-c>
    </div>
    <template id="a">
       <div>
           <h3>A组件:{{name}}</h3>
           <button @click="send">将数据发送给C组件</button>
       </div>
    </template>
    <template id="b">
        <div>
            <h3>B组件:{{age}}</h3>
        </div>
    </template>
    <template id="c">
        <div>
            <h3>C组件:{{name}}</h3>
        </div>
    </template>
    <script>  
        // 定义一个空的Vue实例,触发和监听事件通过这个实例
        var Event=new Vue();

        var A={
                template:'#a',
                data(){
                    return {
                           name:'tom'
                    }
                },
                methods:{
                    send(){  // emit触发事件
                        Event.$emit('data-a',this.name);
                    }
                }
            }
        var B={
                template:'#b',
                data(){
                    return {
                        age:20
                    }
                }
            }
        var C={
                template:'#c',
                data(){
                    return {
                        name:''      
                    }
                },
                // 在模板编译完成后执行
                mounted(){
                    Event.$on('data-a',name =>{
                        this.name=name
                    });
                }
            }
        var vm=new Vue({
            el:'#itany', 
            components:{
                'my-a':A,
                'my-b':B,
                'my-c':C
            }
        });
    </script>
</body>
</html>

  解析

  定义空的Vue示例名称为Event

var Event=new Vue();

  点击按钮调用send方法发送数据

  <button @click="send">将数据发送给C组件</button>

  send方法如下,Event是空的Vue实例,定义事件名称data-a,发送的数据是this.name

methods:{
                    send(){  // emit触发事件
                        Event.$emit('data-a',this.name);
                    }
                }

  在子组件C中接收数据,通过监听的方法,监听刚刚定义的data-a,然后传递的参数为name,把这个传递的值赋值给C组件(需要在C组件的data事先定义空的name属性)

mounted(){
                    Event.$on('data-a',name =>{
                        this.name=name
                    });
                }

  注意:这里不能使用function(name)进行传递参数,这样的this不是C组件的实例本身

  然后展示出来

 

   同理把B组件的数据传递给C

  07.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>非父子组件间的通信</title>
    <script src="js/vue.js"></script>
</head>
<body>
    <div id="itany">
        <my-a></my-a>
        <my-b></my-b>
        <my-c></my-c>
    </div>
    <template id="a">
       <div>
           <h3>A组件:{{name}}</h3>
           <button @click="send">将数据发送给C组件</button>
       </div>
    </template>
    <template id="b">
        <div>
            <h3>B组件:{{age}}</h3>
            <button @click="send">将数据发送给C组件</button>
        </div>
    </template>
    <template id="c">
        <div>
            <h3>C组件:{{name}},{{age}}</h3>
        </div>
    </template>
    <script>  
        // 定义一个空的Vue实例,触发和监听事件通过这个实例
        var Event=new Vue();

        var A={
                template:'#a',
                data(){
                    return {
                           name:'tom'
                    }
                },
                methods:{
                    send(){  // emit触发事件
                        Event.$emit('data-a',this.name);
                    }
                }
            }
        var B={
                template:'#b',
                data(){
                    return {
                        age:20
                    }
                },
                methods:{
                    send(){  // emit触发事件
                        Event.$emit('data-b',this.age);
                    }
                }
            }
        var C={
                template:'#c',
                data(){
                    return {
                        name:'',
                        age:0
                    }
                },
                // 在模板编译完成后执行
                mounted(){
                    Event.$on('data-a',name =>{
                        this.name=name
                    });
                    Event.$on('data-b',age =>{
                        this.age=age
                    });
                }
            }
        var vm=new Vue({
            el:'#itany', 
            components:{
                'my-a':A,
                'my-b':B,
                'my-c':C
            }
        });
    </script>
</body>
</html>

  页面显示

  三,slot内容分发

   本意:位置,槽

  作用:用来获取组件中的原内容

  示例:如果在组件中还有数据需要显示,例如下面代码<my-hello>wbs1722</my-hello>中间的内容wbs1722需要显示

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>slot内容分发</title>
    <script src="js/vue.js"></script>
</head>
<body>
    <div id="itany">
        <my-hello>wbs1722</my-hello>
    </div>    
    <template id='hello'>
        <div>
            <h3>welcome to itany</h3>
            
        </div>
    </template>
    <script>                       
        var vm=new Vue({
            el:'#itany', 
            components:{
                'my-hello':{
                    template:'#hello'
                }
            }
        });
    </script>
</body>
</html>

  没有使用slot这个内容不显示

 

   使用slot在<template>标签内增加标签<slot></slot>即可

 

   页面显示

 

   如果组件之间有多组数据例如两个列表需要分别在不同位置暂时呢,可以给对应的标签定义一个slot名,然后在template中通过name来区分

  day03/08.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>slot内容分发</title>
    <script src="js/vue.js"></script>
</head>
<body>
    <div id="itany">
        <my-hello>
            <ul slot="s1">
				<li>aaa</li>
				<li>bbb</li>
				<li>ccc</li>
			</ul>
			<ol slot="s2">
				<li>111</li>
				<li>222</li>
				<li>333</li>
			</ol>
        </my-hello>
    </div>    
    <template id='hello'>
        <div>
            <slot name="s2"></slot>
            <h3>welcome to itany</h3>
            <slot></slot>
            <slot name="s1"></slot>
        </div>
    </template>
    <script>                       
        var vm=new Vue({
            el:'#itany', 
            components:{
                'my-hello':{
                    template:'#hello'
                }
            }
        });
    </script>
</body>
</html>

  页面显示,通过slot名把内容放在不同的地方

 

 

 

  

posted @ 2021-12-15 16:16  minseo  阅读(54)  评论(0编辑  收藏  举报