美文网首页
Vue 基础 - 组件

Vue 基础 - 组件

作者: 千反田爱瑠爱好者 | 来源:发表于2018-08-24 11:52 被阅读3次
    https://cn.vuejs.org/

    组件

    使用组件可提高代码的复用性

    命名规则

    vue组件中camelCased(驼峰式)命名与kebab­case(短横线命名):

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

    组件注册

    全局注册(所有vue对象都可以使用,但权限太大,容错率低)

    <div id="app">
        <my-component></my-component>
    </div>
    
    <script>
    Vue.component(
        'my-component', {
            template:'<div>我是组件的内容</div>'       // 组件中的内容会被模板替换
        }
    )
    
    var app = new Vue({
        el: "#app",
        data: {
        
        }
    })
    </script>
    

    局部注册(Vue对象中可用)

    var app = new Vue({
        el: '#app',
        components:{
            'my-component':{
                template: '<div>我是组件的内容</div>'
            }
        }
    })
    

    vue组件模板在某些情况下会受到HTML标签的限制:此时可以使用is属性来挂载组件:

    <!-- 比如<table>中只能有<tbody>,<tr>,<td>等元素,所以直接在table中使用组件是无效的, -->
    
    <table>
        <tbody is="my-component"></tbody>
    </table>
    
    1. 必须使用小写字母加“­”命名组件(child、my­componnet);
    2. template中的内容必须被一个DOM元素包括,也可以嵌套;
    3. 在组件的定义中,可以使用除了template之外的其他选项:data、computed、methods...
    4. data必须是一个方法:
    // ...
        'btn-component': {
            template: '<button @click="count++">'{{count}}</button>,
            data: function () {
                return {            // 专属于组件的对象
                    count: 0        
                }
            }
            
        }
    

    数组验证

    Vue中验证的数据类型有:

    • String
    • Number
    • Boolean
    • Object
    • Array
    • Function
    <div id="app">
        <type-component :a="a" :b="b" :d="d" :f="f" :g="g"></type-component>
    </div>
    <script>
        // 数据验证组件
        Vue.component('typeComponent', {
            props: {
                a: Number,
                b: [String, Number],        // 传入的b只允许是String或Number
                c: {
                    type: Boolean,
                    default: true
                },
                d: {
                    type: Number,
                    required: true
    
                },
                e: {
                    type: Array,
                    default: function () {
                        return [];
                    }
                },
                f: {
                    validator: function (value) {
                        return value > 10;
                    }
                },
                g: {
                    type: Function
                }
            },
            template: '<div>{{a}} - {{b}} - {{d}} - {{f}}</div>',
        })
    
        let app = new Vue({
            el: "#app",
            data: {
                a: 1, b: 567, d: 789, f: 99, g: 1111
            }
    
        })
    </script>
    

    组件通信

    父组件向子组件传递数据

    1. 在组件中使用props来从父亲组件接收参数,在props中定义的属性,都可以在组件中直接使用;
    2. 组件中propps来自父级,而datareturn的数据就是组件自己的数据,两种情况作用域就是组件本身,可以在template,computed,methods中直接使用;
    3. props可以设置字符串数组或对象,以使用v­-bind动态绑定父组件来的内容。

    父组件data: {parentMsg: "Hello World!"}
    -> 子组件<bind-component v-bind:msg="parentMsg"></bind-component>
    -> 子组件"bind-component": {props: ["msg"]}
    -> 子组件"child-component": {template: "<div>{{msg}}</div>"}

    <div id="father" style="border: 2px solid chartreuse; height: 160px">
        <h5 style="text-align: center">父组件</h5>
        <child-component msg="来自父组件的内容"></child-component>      <!-- 父组件向子组件传递数据 -->
        <input type="text" v-model="parentMsg">
        <bind-component v-bind:msg="parentMsg"></bind-component>        <!-- 父组件的parentMsg绑定在子组件的msg中 -->
    </div>
    
    <script>
        let app = new Vue({
            el: "#father",
            data: {
                parentMsg: "Hello World!"
            },
            components: {
                "child-component": {
                    template: "<div>{{msg}}</div>",
                    props: ["msg"],
                },
                "bind-component": {
                    template: "<div>{{msg}}</div>",
                    props: ["msg"],
                }
            }
        })
    </script>
    

    单向数据流:

    1. 通过props传递数据是单向的,父组件数据变化时会传递给子组件,但不能反过来;
    2. 单向传递的目的是尽可能将父子组件解稿,避免子组件无意中修改了父组件的状态。

    当父组件传递初始值进来,子组件将它作为初始值保存起来,在自己的作用域下可以随意使用和修改:

    <div id="app">
        <my-component msg="父组件传递到子组件的数据"></my-component>
    </div>
    
    <script>
        // 注册组件
        Vue.component('my-component', {
            props: ['msg'],        // 将父组件的数据传递进来,并在子组件中用props接收
            template: '<div>子组件</div>',
            data: function () {
                return {
                    count: this.msg     // 将传递进来的数据通过初始值保存起来
                    // props中的值可以通过this.XXX直接获取
                }
            }
        })      
        let app = new Vue({
            el: "#father",
            data: {}
        })
    </script>
    

    prop作为需要被转变的原始值传入:

    <div id="app">
    
        <input type="text" v-model="width">
        <my-comp :width="width"></my-comp>
    </div>
    
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                width: '100'
            },
            components: {
                'my-comp': {
                    props: ['width'],
                    template: '<div :style="style">{{width}}px</div>',
                    computed: {
                        style: function () {
                            return {
                                width: this.width + 'px',
                                background: 'red'
                            }
                        }
                    }
                }
            }
        })
    </script>
    

    子组件向父组件传递数据

    子组件<button @click='increase'>+1</button>
    -> 子组件this.$emit('change', this.count)
    -> 父组件<child @change="handldTotal"></child>
    -> 父组件handldTotal: function(value) {this.total = value}

    <div id="app">
        {{total}}
        <child @change="handldTotal"></child>       <!-- 也可以在组件中使用v-model的方法实现 -->
        <!--<child v-model="total"></child>-->
    </div>
    
    <script>
        Vue.component('child', {
            template: "<div><button @click='increase'>+1</button></div>",
            methods: {
                increase: function () {
                    this.count += 1
                    this.$emit('change', this.count)        // 子组件向父组件发出发生change事件的通知
                    // this.$emit('input', this.count)      // 使用v-model时指定为input事件通知
                }
            },
            data: function () {
                return {
                    count: 200
                }
            }
        })
        let app = new Vue({
            el: "#app",
            data: {
                total: 200
            },
            methods: {
                handldTotal: function(value) {
                    this.total = value
                }
            }
        })
    </script>
    
    1. $emit实际上会触发一个input事件, 其后的参数就是传递给v­model绑定的属性的值
    2. v­model其实是一个语法糖,这背后做了两个操作:
      i. v­bind绑定一个value属性
      ii. v­on指令给当前元素绑定input事件

    非父组件之间的通信

    可以使用一个空的Vue实例作为中央事件总线:


    <div id="app">
        <a-component></a-component>
        <b-component></b-component>
        <br>
        <child-component></child-component>
        {{msg}}
    </div>
    
    <script>
        Vue.component('a-component', {
            template: '<div style="width: 100px; height: 100px; border: 1px solid black"><button @click="handle">点击向B组件传递数据</button></div>',
            data: function () {
                return {
                    a: '来自A组件的内容'
                }
            },
            methods: {
                handle: function () {
                    this.$root.bus.$emit('send', this.a)
                }
            }
        })
        Vue.component('b-component', {
            template:'<div style="width: 100px; height: 100px; border: 1px solid black">{{b}}</div>',
            data: function() {
                return {
                    b: ""
                }
            },
            created: function () {
                let self = this
                this.$root.bus.$on('send', function (value) {
                    self.b = value
                })
            }
        })
    
        Vue.component('child-component', {
            template: '<button @click="setFatherData">通过点击修改父亲的数据</button>',
            methods: {
                setFatherData: function () {
                    this.$parent.msg = '数据已修改'
                }
            }
        })
    
        let app = new Vue({
            el: "#app",
            data: {
                bus: new Vue(),
                msg: '数据未修改',
                formchild: '未取得数据'
            },
            methods: {
                getChildData: function() {
                    this.formchild = this.$refs.c.msg;
                }
            }
        })
    </script>
    

    使用Slot分发内容

    让组件可以组合的过程被称为内容分发,使用特殊的slot元素作为原始内容的插槽。

    编译的作用域

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

    其中message应该绑定到父组件的数据,组件作用域简单地说是:

    • 父组件模板的内容在父组件作用域内编译;
    • 子组件模板的内容在子组件作用域内编译。

    插槽的用法

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

    <div id="app">
        <my-component>
            <p>我是父组件的内容</p>
            <!-- 最外层组件内部的所有内容都由最外层组件控制 -->
            <!-- 如果没有使用slot,则子组件无法在其内部插入内容 -->
        </my-component>
    </div>
    <script>
        Vue.component('my-component', {
            template:
                `<div>
                    <slot>如果父组件没有插入内容,我就作为默认出现</slot>
                </div>`
        })    
        new Vue({
            el: "#app"
    
        })
    </script>
    

    其中<slot>标签中可以指定name,称为“具名插槽”,对应DOM标签的slot属性:

    <p slot="footer">底部</p>
    
    // ...
    template: `
        <div class="footer">
            <slot name="footer"></slot>
        </div>`
    

    作用域插槽

    使用一个可以复用的模板来替换已经渲染的元素

    从子组件获取数据

    <div id="app">
        <my-component>
            <template slot="abc" slot-scope="prop">    <!-- template是不会被渲染的,2.5.0版本后不需要写template -->
                {{prop.text}}       <!-- text在slot中定义 -->
            </template>
        </my-component>
    </div>
    <script>
        Vue.component('my-component', {
            template: `
                <div>
                    <slot text="来自子组件的数据" name="abc"></slot>
                </div>
            `
        })
        new Vue({
            el: "#app"
        })
    </script>
    

    访问插槽

    通过this.$slots.name可以访问名称为name的插槽

    mounted:function () {
    // 访问插槽
        var header = this.$slots.header;
        var text = header[0].elm.innerText;
        var html = header[0].elm.innerHTML;
        console.log(header)
        console.log(text)
        console.log(html)
    }
    

    动态组件

    Vue提供了component元素用来动态的挂载不同的组件,使用is特性实现。

    <div id="app">
        <!-- 点击不同按钮实现切换不同组件 -->
        <component :is="thisView"></component>
        <button @click="handleView('A')">1</button>
        <button @click="handleView('B')">2</button>
        <button @click="handleView('C')">3</button>
    </div>
    <script>
        Vue.component('compA', {
            template: '<div>JavaScript</div>'
        })
        Vue.component('compB', {
            template: '<div>CSS</div>'
        })
        Vue.component('compC', {
            template: '<div>HTML</div>'
        })
        let app = new Vue({
            el: "#app",
            data: {
                thisView: "compA"
            },
            methods: {
                handleView: function (tag) {
                    this.thisView = "comp" + tag;
                }
            }
        })
    </script>
    

    相关文章

      网友评论

          本文标题:Vue 基础 - 组件

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