🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
![](https://img.kancloud.cn/41/e0/41e066af9a6c25a24868d9667253ec98_1241x333.jpg) ***** ## 自定义组件 有时候有一组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":"钢铁是怎样练成的?","id":1}, {"title":"AI会毁灭人类吗?","id":2}, {"title":"如何学好Vue!","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> ``` 需要注意的是,因为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> ``` 当组件渲染的时候,<slot></slot>将会被替换为“个人中心”。插槽内可以包含任何模板代码,包括HTML: ``` <navigation-link url="/profile"> <!-- 添加一个 Font Awesome 图标 --> <span class="fa fa-user"></span> 个人中心 </navigation-link> ``` 如果<navigation-link>没有包含一个<slot>元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。