美文网首页饥人谷技术博客
Vue可复用性的组件详解

Vue可复用性的组件详解

作者: 飞天小猪_pig | 来源:发表于2021-06-30 21:41 被阅读0次
    1、组件的使用方法
    1. 全局注册
     Vue.component('my-component', {
            template: '<div>我是一个组件</div>'
        })
        var app = new Vue({
            el: '#app',
            data: {
    
            },
        })
    

    优点:所有的vue实例都可以用
    缺点:权限太大,容错率降低

    1. 局部注册
     var bpp = new Vue({
            el: '#bpp',
            components: {     //注意这里的components是复数
                'bpp-component': {
                    template: '<div>我是bpp里面的组件</div>'
                }
            },
            data: {
    
            }
    
    1. vue组件的模板在某些情况下会受到html标签的限制,比如 <table>中只能有 <tr>, <td> 这些元素,所以直接在table中使用组件是无效的,此时可以使用is属性来挂载组件
    <table>
    <tbody is="my-component"></tbody>
    </table>
    
    2、组件使用的小技巧

    1、组件名字推荐使用小写字母加­-进行命名,(必须)小写child, 或者my­-componnet命名组件,不能出现大写形式。
    2、 template中的内容必须被一个DOM元素包括 ,也可以嵌套使用
    3、 在组件的定义中,除了template之外还有其他选项如:data、computed、methods
    4、组件里面的data必须是一个方法形式

    需求:实现两个按钮各自点击能实现加1,互不影响

     <div id='app'>
        <my-component></my-component>
        <my-component></my-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    <script>
        var app = new Vue({
            el: '#app',
            components: {
                'my-component': {
                    template: '<button @click="count++">{{count}}</button>',
                    data: function () {
                        return {
                            count: 0
                        }
                    }
                },
            }
        })
    </script>
    
    3、使用props传递数据(父亲向儿子传递数据)
    1. 在组件中使用props来从父亲组件接收参数,注意,在props中定义的属性,都可以在组件中直接使用
    <div id='app'>
        <h5>我是父组件</h5>
        <my-component msg='我是来自父组件内容'></my-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    <script>
        var app = new Vue({
            el: '#app',
            components:{
                'my-component':{
                    props:['msg'],
                    template:'<div>{{msg}}</div>'
                }
            }
        })
    </script>
    
    1. props来自父级,而组件中data中return的数据就是组件自己的数据,两种情况作用域就是组件本身,可以在templatecomputedmethods中直接使用。

    3、可以使用v-­bind动态绑定父组件来的内容

    <div id="app" style="border:1px solid red;height:150px">
        <h5>我是父组件</h5>
        <input type="text" v-model='parentmsg' style="border-bottom:10px">
        <my-component v-bind:msg="parentmsg"></my-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                parentmsg: '今天的太阳真热'
            },
            components: {
                'my-component': {
                    props: ['msg'],
                    template: '<div style="border:1px solid green;height:50px">{{msg}}</div>'
                }
            }
        })
    </script>
    
    image.png

    3.props的值有两种,一种是字符串数组,一种是对象,本节先只讲数组

     <div id="app" style="border:1px solid red;height:150px">
        <h5>我是父组件</h5>
        <my-component msg=[3,6,9]></my-component>
        <my-component :msg=[3,6,9]></my-component>  //利用v-bind进行绑定
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    <script>
        var app = new Vue({
            el: '#app',
            components: {
                'my-component': {
                    props: ['msg'],
                    template: '<div style="border:1px solid green;height:30px">
                               {{msg}}--{{msg.length}}</div>'  
                }
            }
        })
    </script>
    
    image.png
    能看出来利用v-bind进行数组数据传输和没有v-bind的区别,所以当我们进行数组数据传递时要绑定v-bind,才能确保数组长度没有发生改变。
    4、单向数据流

    1、解释 : 通过 props 传递数据是单向的了, 也就是父组件数据变化时会传递给子组件,但是反过来不行。
    2、目的: 是尽可能将父子组件解稿,避免子组件无意中修改了父组件的状态。
    3、应用场景: 业务中会经常遇到两种需要改变 prop 的情况

    场景一:是父组件传递初始值进来,子组件将它作为初始值保存起来,在自己的作用域下可以随意使用和修改。这种情况可以在组件 data 内再声明一个数据,引用父组件的 prop
    步骤一:注册组件
    步骤二:将父组件的数据传递进来,并在子组件中用props接收
    步骤三:将传递进来的数据通过初始值保存起来

    <div id="app">
        <my-component msg='我是父组件传递的数据'></my-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    <script>
        var app = new Vue({
            el: '#app',
            components: {
                'my-component': {
                    props: ['msg'],
                    template: '<div>{{count}}</div>',
                    data: function () {
                        return {
                            //props中的值可通过this.xxx来获取
                            count: this.msg
                        }
                    }
                }
            }
        })
    </script>
    

    场景二:就是 prop 作为需要被转变的原始值传入。这种情况用计算属性就可以了
    步骤一:注册组件
    步骤二:将父组件的数据传递进来,并在子组件中用props接收
    步骤三:将传递进来的数据通过计算属性进行重新计算

     <div id="app">
        <input type="text" v-model="width">
        <my-component :width="width"></my-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                width: 0
            },
            components: {
                'my-component': {
                    props: ['width'],
                    template: '<div :style="style"></div>',
                    computed: {
                        style: function () {
                            return {
                                width: this.width + "px",
                                height: '30px',
                                background: 'red'
                            }
                        }
                    }
                }
            }
        })
    </script>
    
    image.png
    5、数据验证

    vue组件中的camelCased(驼峰式)命名与kebab-case(短横线命名)规则

    1、在html中, myMessage 和 mymessage 是一致的,,因此在组件中的html中使用必须使用kebab-­case(短横线)命名方式。在html中不允许使用驼峰!!!!!!
    2、在组件中, 父组件给子组件传递数据必须用短横线。在template中,必须使用驼峰命名方式,若为短横线的命名方式。则会直接保错。
    3、 在组件的data中,用this.XXX引用时,只能是驼峰命名方式。若为短横线的命名方式,则会报错。

    1、验证Number类型

    <div id="app">
        <my-component :a='a' ></my-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    <script>
        var app = new Vue({
            el: '#app',
            components: {
                'my-component': {
                    props: {
                        a: Number,
                    },
                    template: '<div>{{a}}</div>',
                },
            },
            data: {
                a: 1,
            }
        })
    </script>
    

    2、验证String类型

    <div id="app">
        <my-component :a='a' ></my-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    <script>
        var app = new Vue({
            el: '#app',
            components: {
                'my-component': {
                    props: {
                        a: String,
                    },
                    template: '<div>{{a}}</div>',
                },
            },
            data: {
                a: 'abc',   
            }
        })
    </script>
    

    3、验证StringNumber类型

    <div id="app">
        <my-component :a='a' ></my-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    <script>
        var app = new Vue({
            el: '#app',
            components: {
                'my-component': {
                    props: {
                        a: [Number,String]
                    },
                    template: '<div>{{a}}</div>',
                },
            },
            data: {
                a: 2,   //或者改成字符串a:'qwe'都不会报错
            }
        })
    </script>
    

    4、验证Boolean类型

    <div id="app">
        <my-component :a='a' ></my-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    <script>
        var app = new Vue({
            el: '#app',
            components: {
                'my-component': {
                    props: {
                        a: {
                            type:Boolean,
                            default:true,  //布尔值,如果没有定义,默认值就是 true
                         },
                    },
                    template: '<div>{{a}}</div>',
                },
            },
            data: {
                a: true,
            }
        })
    </script>
    

    5、验证Number类型是必传的

    <div id="app">
        <my-component :a='a' ></my-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    <script>
        var app = new Vue({
            el: '#app',
            components: {
                'my-component': {
                    props: {
                        a: {
                            type:Number,
                            required:true,
                         },
                    },
                    template: '<div>{{a}}</div>',
                },
            },
            data: {
                a: 111,
            }
        })
    </script>
    

    6、验证Array类型

    <div id="app">
        <my-component :a='a' ></my-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    <script>
        var app = new Vue({
            el: '#app',
            components: {
                'my-component': {
                    props: {
                        a: {
                            type:Array,
                            default:function(){
                                return [555]   //默认为数值[555]
                            }
                         },
                    },
                    template: '<div>{{a}}</div>',
                },
            },
            data: {
                a: [444],
            }
        })
    </script>
    

    7、验证Function类型

    <div id="app">
        <my-component :a='a' ></my-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    <script>
        var app = new Vue({
            el: '#app',
            components: {
                'my-component': {
                    props: {
                        a: {
                            type:Function,
                         },
                    },
                    template: '<div>{{a}}</div>',
                },
            },
            data: {
                a: console.log(),
            }
        })
    </script>
    

    8、自定义一个验证函数

    <div id="app">
        <my-component :a='a' ></my-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    <script>
        var app = new Vue({
            el: '#app',
            components: {
                'my-component': {
                    props: {
                        a: {
                         validator:function(value){     //validator验证器名字
                                 return value>22
                          }
                         },
                    },
                    template: '<div>{{a}}</div>',
                },
            },
            data: {
                a: 33,
            }
        })
    </script>
    
    6、组件通信

    组件关系可分为父子组件通信、兄弟组件通信、跨级组件通信

    6.1 自定义事件—子组件给父组件传递数据

    使用v­-on 除了监昕 DOM 事件外,还可以用于组件之间的自定义事件。JavaScript 的设计模式--观察者模式, dispatchEventaddEventListener这两个方法。 Vue 组件也有与之类似的一套模式,子组件用$emit()来触发事件,父组件用$on()来监昕子组件的事件 。

    第一步:自定义事件
    第二步: 在子组件中用$emit触发事件,第一个参数是事件名,后边的参数是要传递的数据
    第三步:在自定义事件中用一个参数来接受来自子组件数据

    <!-- 需求:通过加、减按钮实现子组件给父组件传递数据 -->
    <div id="app">
        你现在的总余额是{{total}}元
        <son-component @change='handleTotal'></son-component> //change是自定义事件
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    <script>
        var app = new Vue({
            el: '#app',
            components: {
                'son-component': {
                    template: '<div>\
                            <button @click=handleincrease>+</button>\
                            <button @click=handleruduce>-</button>\
                               </div>',
                    data: function () {
                        return {
                            count: 1000
                        }
                    },
                    methods: {
                        handleincrease: function () {
                            this.count = this.count + 1000,
                                this.$emit('change', this.count)
                        },
                        handleruduce: function () {
                            this.count = this.count - 1000,
                                this.$emit('change', this.count)
                        }
                    }
                },
            },
            data: {
                total: 1000,
            },
            methods: {
                handleTotal: function (value) {
                    this.total = value
                }
            }
        })
    </script>
    
    6.2 在组件中使用v­-model

    对比6.1中的例子使用v-model实现需求

    <!-- 需求:通过加、减按钮实现子组件给父组件传递数据 -->
    <div id="app">
        你现在的总余额是{{total}}元
        <son-component v-model='total'></son-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    <script>
        var app = new Vue({
            el: '#app',
            components: {
                'son-component': {
                    template: '<div>\
                            <button @click=handleincrease>+</button>\
                            <button @click=handleruduce>-</button>\
                               </div>',
                    data: function () {
                        return {
                            count: 1000
                        }
                    },
                    methods: {
                        handleincrease: function () {
                            this.count = this.count + 1000,
                                this.$emit('input', this.count)
                        },
                        handleruduce: function () {
                            this.count = this.count - 1000,
                                this.$emit('input', this.count)
                        }
                    }
                },
            },
            data: {
                total: 1000,
            },
        })
    </script>
    

    v­-model 其实是一个语法糖,这背后其实做了两个操作
    **v-­bind 绑定一个 value 属性,实现动态更新
    **v­-on 指令给当前元素绑定 input 事件,指定触发事件名
    要使用v-­model,要做到:接收一个 value 属性,在有新的 value 时触发 input 事件

    6.3 非父组件之间的通信

    有时候两个组件也需要通信(非父组件),在简单的场景下,可以使用空的Vue实例作为中央事件总线进行。

    1

    var bus=new Vue()
    

    2

    //触发组件A中的事件
    bus.$emit('id-selected',2)
    

    3

    //在组件B创建的钩子中监听事件
    bus.$on('id-selected',function(){
         .....
    })
    
    image.png

    1、点击a组件向b组件传递数据(非父组件)

    <div id="app">
        <my-acomponent></my-acomponent>
        <my-bcomponent></my-bcomponent>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    <script>
        Vue.component('my-acomponent',{
            template:'<div><button @click="handle">点我向b组件传递数据</button></div>',
            data:function(){
                return{
                    a:'我是来自a组件内容'
                }
            },
           methods:{
               handle:function(){
                   this.$root.bus.$emit('lalala',this.a)
               }
           }
        })
        Vue.component('my-bcomponent',{
            template:'<div></div>',
            created:function( ){
               this.$root.bus.$on('lalala',function(value){
                    alert(value)
               })
            }
        })
        var app = new Vue({
            el: '#app',
            data: {
              bus:new Vue(),
            },
        })
    </script>
    

    注意:这里通过this.$root访问是根父组件的内容,而通过this.parent访问的事最近一级父组件内容

    2、点击子组件按钮修改父组件内容

    <div id="app">
        <my-acomponent></my-acomponent>
        {{msg}}
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    <script>
        Vue.component('my-acomponent',{
            template:'<div><button @click="handle">点击我修改父亲的数据</button></div>',
           methods:{
               handle:function(){
                   this.$parent.msg='数据已修改'
               }
           }
        })
        var app = new Vue({
            el: '#app',
            data: {
              msg:'数据未修改',
            },
        })
    </script>
    

    注意:这里通过this.parent访问的事最近一级父组件内容

    3、在父组件中拿到子组件的数据
    Vue提供了为子组件提供索引的方法,用特殊的属性ref为其增加一个索引

    <div id="app">
        <my-acomponent ref='a'></my-acomponent>
        <my-bcomponent ref='b'></my-bcomponent>
        <button @click='getchilddata'>点击我拿到子组件的数据</button>
        {{formchild}}
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    <script>
        Vue.component('my-acomponent',{
            template:'<div></div>',
            data:function(){
                return{
                    msg:'我是a中的msg'
                }
            },
        })
        Vue.component('my-bcomponent',{
            template:'<div></div>',
            data:function(){
                return{
                    msg:'我是b中的msg'
                }
            },
        })
        var app = new Vue({
            el: '#app',
            data: {
              formchild:'还未拿到数据'
            },
            methods:{
               getchilddata:function(){
                   this.formchild=this.$refs.a.msg   //注意这里是refs不是ref
               }
            }
        })
    </script>
    

    注意:如果父组件有多个子组件不能通过this.$child来获取子组件内容,而是通过给子组件添加索引,通过this.refs获取

    7、使用slot分发内容
    7.1、什么是slot(插槽)

    为了让组件可以组合,我们需要一种方式来混合父组件的内容与子组件自己的模板。这个过程被称为内容分发。Vue.js 实现了一个内容分发 API,使用特殊的 slot元素作为原始内容的插槽。

    7.2、 编译的作用域

    在深入内容分发 API 之前,我们先明确内容在哪个作用域里编译。假定模板为:

    <child-component>
    {{ message }}
    </child-component>
    

    message 应该绑定到父组件的数据,还是绑定到子组件的数据?答案是父组件。

    组件作用域简单地说是:
    父组件模板的内容在父组件作用域内编译;
    子组件模板的内容在子组件作用域内编译。

    7.3 、插槽的用法

    父组件的内容与子组件相混合,从而弥补了视图的不足

    1、单个插槽:

    <div id="app">
        <my-component>
           <p>我是父组件的内容</p>     //这是父组件插入的内容
        </my-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    <script>
        Vue.component('my-component',{
            template:'<div>\
                         <slot>\
                         如果父组件没有插入内容,我就作为默认值出现\
                         </slot>\
                      </div>',
        })
        var app = new Vue({
            el: '#app',
            data: {},
        })
    </script>
    

    2、具名插槽

     <div id="app">
        <my-component>
           <h3 slot=header>标题</h3>    //标记
           <p>内容</p>
           <p slot=footer>结尾</p>
        </my-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    <script>
        Vue.component('my-component', {
                template: '<div>\
                              <div class="header">\
                                  <slot name="header">\    //关联
                                  </slot>\
                              </div>\
                              <div class="container">\
                                  <slot>\
                                  </slot>\
                              </div>\
                              <div class="footer">\
                                   <slot name="footer">\
                                   </slot>\
                              </div>\
                           </div> ',
            })
        var app = new Vue({
            el: '#app',
            data: {},
        })
    </script>
    
    7.4 、作用域插槽

    作用域插槽是一种特殊的slot,使用一个可以复用的模板来替换已经渲染的元素
    ------从子组件获取数据
    ------template模板是不会被渲染的

    <div id="app">
        <my-component>
          <template slot="abc" slot-scope="prop">
              {{prop.text}}
              {{prop.ss}}    //子组件中的name中数据是关联用得,不能通过这样获取
          </template>>
        </my-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    <script>
        Vue.component('my-component', {
                template: '<div>\
                             <slot text="我是子组件的数据" ss="sdfsd" name="abc">\
                             </slot>\
                           </div> ',
            })
        var app = new Vue({
            el: '#app',
            data: {},
        })
    </script>
    
    7.5、 访问slot

    通过this.$slots.(NAME)

     <div id="app">
                <my-component>
                   <h3 slot=header>标题</h3>   
                   <p slot=footer>结尾</p>
                </my-component>
            </div>
            <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
            <script>
                Vue.component('my-component', {
                        template: '<div>\
                                      <div class="header">\
                                          <slot name="header">\
                                          </slot>\
                                      </div>\
                                      <div class="footer">\
                                           <slot name="footer">\
                                           </slot>\
                                      </div>\
                                   </div> ',
                        mounted:function(){
                            var header=this.$slots.header
                            var text=header[0].elm.innerText  //由DOM结构得来
                            console.log(header)  //查看DOM结构
                            console.log(text)
                        }
                    })
                var app = new Vue({
                    el: '#app',
                    data: {},
                })
            </script>
    
    7.6、 组件高级用法–动态组件

    VUE给我们提供 了一个元素叫component
    作用是: 用来动态的挂载不同的组件
    实现:使用is特性来进行实现动态组件

    1、需求:通过点击不同按钮切换不同视图

     <div id="app">
             <component :is=thisview></component>
             <button @click="handleview('A')">第一句</button>
             <button @click="handleview('B')">第二句</button>
            </div>
            <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
            <script>
                Vue.component('compA', {
                     template:'<div>山不在高<div>'
                    })
                    Vue.component('compB', {
                     template:'<div>有仙则名<div>'
                    })
                var app = new Vue({
                    el: '#app',
                    data: {
                        thisview:'compA',
                    },
                    methods:{
                        handleview:function(tag){
                               this.thisview='comp'+tag
                        }
                    }
                })
            </script>

    相关文章

      网友评论

        本文标题:Vue可复用性的组件详解

        本文链接:https://www.haomeiwen.com/subject/aryacltx.html