Vue 自定义组件

项目前导 学习笔记

一、自定义组件


        有时候有一组 html 结构的代码,且其可能还绑定了事件。然后这段代码可能有多个地方都被使用到了,如果都是拷贝来拷贝去,很多代码都是重复的,包括事件部分的代码都是重复的。那么这时候我们就可以把这些代码封装成一个组件,以后在使用的时候就和使用普通的 html 标签 一样,直接拿过来用就可以了(且不相互影响)。

注意:使用 Vue.component 创建


1.1、基本使用

	<div id="app">
	    <button-counter></button-counter>
	    <button-counter></button-counter>
	    <button-counter></button-counter>
	</div>


	<script>
	    Vue.component(
	    	//  自定义组件( button-counter 为自定义的标签名 )
	        'button-counter', {
	            template: '<button @click="co	unt+=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 必须为一个函数!



1.2、给组件添加属性

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

	<div id="app">
		<!--  通过自定义组件添加属性实现  -->
	    <blog-post v-bind:lis="blogs"></blog-post>
	</div>


	<script>
	    Vue.component(
	    	'blog-post', {
		        props: ['lis'],
		        
		        //  包含多行要用 `` 括起来
		        template:
		        `
		        <table>
		            <tr>
		                <th>序号</th>  
		                <th>标题</th>  
		            </tr>  
		            
                	// 这里循环的应是组件定义的属性, 该属性相当于是参数 传入对象 blogs
		            <tr v-for="(blog,index) in lis">
		                <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>



1.3、单一根元素

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

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

应该改成:

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



1.4、事件传递

        子组件(自己定义的组件)中添加事件跟之前的方式是一样的,然后如果发生某个事件后想要通知父组件(new Vue),那么可以使用 this.$emit 函数来实现。

  • 例:点击选择,显示已选项,再次点击取消选择
	<div id="app">
	    <blog-item v-for="i in blogs" v-bind:blog="i" @check-changed="checks"></blog-item>

	    <div v-for="n in componentblog">
	        {{n.title}}
	    </div>
	</div>


	<script>
	    Vue.component('blog-item',{
	        props:['blog'],
	        
            //  同样的, 这里的 blog 都是组件的属性

	        template:`
	        <div>
	            <span>{{blog.title}}</span>
	            <input type="checkbox" @click="onCheck">   
	        </div>
	        `,
	        
	        methods:{
	            onCheck:function(){
	                // console.log(123)
	                // 'check-changed' 即为组件触发事件的属性
	                // 可以理解为子组件(自定义组件)通知父组件(上面HTML代码,即new Vue)执行 check-changed 的事件(看上面代码)
	                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 判断某个元素在数组中的位置, 返回下标(下标从0开始)
	                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 这种规则。



1.5、自定义组件 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:{
	        	// 什么情况下触发这个 v-model 的行为
	            event: 'count-changed',
                // 处理的对象
	            prop: "count"
	        },
	        template:`
	        <div>
	            <button @click="sub">-</button>  
	            <span>{{count}}</span>
	            <button @click="add">+</button>  
	        </div>
	        `,	
	        
	        methods:{
	            sub:function(){
	            	// 也可以判断一下 if (this.count > 0)
	            	// 触发顺序   点击 → sub() → <stepper> → v-model → count → goods_count
	                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 ,后面触发哪个事件的时候要修改属性。



1.6、插槽

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

	<div id="app">
	    <navigation-link :url="url">
	    	<!--  在这添加的位置就是 slot 留出来的插槽  -->
	        个人中心
	    </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",
	        data: {
	        	'url': "https://www.baidu.com/"
	        }
	    });
	</script>

如果要添加多个插槽,那么就要为每个插槽命名,否则所有插槽都会显示相同的内容。

	<navigation-link url="/profile">
	  	个人中心
	</navigation-link>

	...
	<slot>关于</slot>
	<slot>关于</slot>
	<slot>关于</slot>
	...

起个名字,用 v-slot: 引用名字,没有的话就是默认的名字 default

    <navigation-link :url="url">
    	<!--  在这添加的位置就是 slot 留出来的插槽  -->
           <!--  个人中心  -->
           
           <!--  要用 template 标签  -->
           <template v-slot:one>第一个</template>
           <template>第二个</template>
           <template v-slot:three>第三个</template>
    </navigation-link>


	...
	<a v-bind:href="url" class="nav-link">
		<!-- 插槽的优先级高于模板的优先级  -->
		<!--  为了好看点 可以加个 div  -->
		<div> <slot name="one"></slot> </div>
	    <div> <slot name="default"></slot> </div>
	    <div> <slot name="three"></slot> </div>
	</a>
	...



1.7、插槽的作用域

    <div id="app">
        <container>
            <!--  其中 h 包含模板中调用的所有数据对象  -->
            <template v-slot:header="h">
                <!--  使用 h.属性名 来取得调用的值  -->
                头部区域:{{h.hva}} -> {{h.hf}}
            </template>
            <template v-slot:footer="f">
                尾部区域:{{f.fva}}
            </template>
        </container>
    </div>


	<script>
	    Vue.component(
	        'container', {
	            template:
	            `
	            <div>
	                <!--  使用属性调用定义的数据对象  -->
	                <div><slot name="header" :hva="navs" :hf="addr"></slot></div>
	                <div><slot name="footer" :fva="addr"></slot></div>
	            </div>
	            `,
	
	            data(){
	                return {
	                    navs: ['新闻', 'hao123', '地图'],
	                    addr: "百度"
	                }
	            }
	        }
	    );
	
	    new Vue({
	        el: "#app"
	    });
	</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值