自定义组件

124 阅读4分钟

有时候有一组html结构的代码,并且这个上面可能还绑定了事件。然后这段代码可能有多个地方都被使用到了,如果都是拷贝来拷贝去,很多代码都是重复的,包括事件部分的代码都是重复的。那么这时候我们就可以把这些代码封装成一个组件,以后在使用的时候就跟使用普通的html元素一样,拿过来用就可以了。

##基本使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <div>
            <button-counter></button-counter>
            <button-counter></button-counter>
            <button-counter></button-counter>
        </div>
    </div>
</body>
</html>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    Vue.component('button-counter',{
        template:'<button v-on:click="count+=1">点击了{{ count }}次</button>',
        data:function(){
            return {
                count: 0
            }
        }
    });
    new Vue({
        el:"#app",
        data:{

        }
    })
</script>

以上我们创建了一个叫做button-counter的组件,这个组件实现了能够记录点击了多少次按钮的功能。后期如果我们想要使用,就直接通过button-counter使用就可以了。然后因为组件是可复用的Vue实例,所以它们与new Vue接收相同的选项,例如data、computed、watch、methods以及生命周期钩子等。仅有的例外是像el这样根实例特有的选项。另外需要注意的是:组件中的data必须为一个函数!

##给组件添加属性 像原始的html元素都有自己的一些属性,而我们自己创建的组件,也可以通过prop来添加自己的属性。这样别人在使用你创建的组件的时候就可以传递不同的参数了.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <div>
            <book-list v-bind:books="books"></book-list>
        </div>
    </div>
</body>
</html>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<script>
    Vue.component("book-list",{
        props:['books'],
        template:`
        <table>
                <tr>
                    <th>序号</th>
                    <th>标题</th>
                </tr>
                <tr v-for="(book,index) in books">
                    <td>{{index+1}}</td>
                    <td>{{book.title}}</td>
                </tr>
            </table>
            `
    })
    new Vue({
        el:"#app",
        data:{
            books:[
                {"title":"python","id":1},
                {"title":"php","id":2},
                {"title":"java","id":3},
            ]
        }
    })
</script>

##单一根元素 如果自定义的组件中,会出现很多html元素,那么根元素必须只能有一个,其余的元素必须包含在这个根元素中。比如以下是一个组件中的代码,会报错:

<h3>{{ title }}</h3>
<div v-html="content"></div>

我们应该改成:

<div class="blog-post">
  <h3>{{ title }}</h3>
  <div v-html="content"></div>
</div>

##子组件事件和传递事件到父组件 子组件中添加事件跟之前的方式是一样的,然后如果发生某个事件后想要通知父组件,那么可以使用this.$emit函数来实现。

<div id="app">
    <blog-item v-for="blog in blogs" v-bind:blog="blog" @check-changed="checks"></blog-item>    
    
    <div v-for="blog in componentblog">
        {{blog.title}}
    </div>
</div>
<script>
    Vue.component('blog-item',{
        props:['blog'],
        template:`
        <div>
            <span>{{blog.title}}</span>
            <input type="checkbox" @click="onCheck">   
        </div>
        `,
        methods:{
            onCheck:function(){
                // console.log(123)
                this.$emit('check-changed',this.blog)
            }
        }
    })

    new Vue({
        el: '#app',
        data: {
            blogs:[
                {"title":"钢铁是怎样练成的?","id":1},
                {"title":"AI会毁灭人类吗?","id":2},
                {"title":"如何学好Vue!","id":3},
            ],
            componentblog:[]
        },
        
        methods:{
            checks:function(blog){
                // indexOf 判断某个元素在数组中的位置,返回下标
                var index = this.componentblog.indexOf(blog)
                if(index >= 0){
                    this.componentblog.splice(index,1)
                }else{
                    this.componentblog.push(blog)
                }
                console.log(blog)
            }
        }
    })
</script>

##自定义组件v-model 一个组件上的v-model默认会利用名为value的prop(属性)和名为input的事件,但是像单选框、复选框等类型的输入控件可能会将value特性用于不同的目的。这时候我们可以在定义组件的时候,通过设置model选项可以用来实现不同的处理方式

<div id="app">
    <stepper v-model:value="goods_count"></stepper>
</div>

<script>
     Vue.component('stepper',{
        props:['count'],
        model:{
            event: 'count-changed',
            prop: "count"
        },
        template:`
        <div>
            <button @click="sub">-</button>  
            <span>{{count}}</span>
            <button @click="add">+</button>  
        </div>
        `,
        methods:{
            sub:function(){
                this.$emit("count-changed", this.count-1)
            },
            add:function(){
                this.$emit("count-changed", this.count+1)
            }
        }
    });

    new Vue({
        el: "#app",
        data:{
            "goods_count":0
        }
    })
</script>

##插槽 我们定义完一个组件后,可能在使用的时候还需要往这个组件中插入新的元素或者文本。这时候就可以使用插槽来实现。

<div id="app">
    <navigation-link url="/profile/">
        个人中心
    </navigation-link>
</div>
<script>
    Vue.component('navigation-link', {
        props: ['url'],
        template: `
        <a v-bind:href="url" class="nav-link">
            <slot></slot>
        </a>
        `
    })
    new Vue({
        el: "#app"
    });
</script>

同时,可以给插槽命名