Vue 自定义组件

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

基本使用

<div id="app">
    <button-counter></button-counter>
    <button-counter></button-counter>
    <button-counter></button-counter>
</div>
<script>
    Vue.component('button-counter', {
        template: '<button v-on:click="count+=1">点击了{{ count }}次</button>'
        data: function(){
            return {
                count: 0
            }
        },
    });
    let vm = new Vue({
        el: "#app",
        data: {}
    });
</script>

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

给组件添加属性

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

<div id="app">
    <blog-post v-for="blog in blogs" :title="blog.title"></blog-post>
    <blog-post v-bind:blogs="blogs"></blog-post>
</div>
<script>
    Vue.component('blog-post', {
        props: ['blogs'],
        template: `
        <table>
            <tr>
                <th>序号</th>  
                <th>标题</th>  
            </tr>  
            <tr v-for="(blog,index) in blogs">
                <td>{{index+1}}</td>
                <td>{{blog.title}}</td>
            </tr>
        </table>
        `
    })
    new Vue({
        el: "#app",
        data: {
            blogs: [
                {"title":"Python","id":1},
                {"title":"Js","id":2},
                {"title":"Vue","id":3},
            ]
        }
    });
</script>

单一根元素

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

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

解决这个问题我们可以将所有的标签都放在一个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":"Python","id":1},
                {"title":"Js","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>

需要注意的是,因为html中大小写是不敏感的,所以在定义子组件传给父组件事件名称的时候,不要使用myEvent这种驼峰命名法,而是使用my-event这种规则。

自定义组件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>

其中的props定义的属性分别是给外面调用组件的时候使用的。model中定义的prop:'count’是告诉后面使用v-model的时候,要修改哪个属性;event:'count-changed’是告诉v-model,后面触发哪个事件的时候要修改属性。

插槽

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

<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>

当组件渲染的时候,将会被替换为“个人中心”。插槽内可以包含任何模板代码,包括HTML:

<navigation-link url="/profile">
  <!-- 添加一个 Font Awesome 图标 -->
  <span class="fa fa-user"></span>
  个人中心
</navigation-link>

如果没有包含一个元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。

如果使用多个插槽,可以使用template

<slot name='xxx' :navs='navs'></slot>

<template v-slot:xxx>
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值