• vue组件学习


    一.文章导读

    ​ 前面学习了vue的基础指令,了解了相关的钩子函数,这一章学习vue的组件,组件 (Component) 是 Vue.js 最强大的功能之一,组件可以扩展 HTML 元素,封装可重用的代码,组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树:

    菜鸟教程地址:https://www.runoob.com/vue2/vue-tutorial.html

    官网学习地址:https://cn.vuejs.org/

    入门学习更加推荐菜鸟

    二.组件定义

    1.全局组件

    <body>
    		<!-- 创建两个承载容器 -->
    		<div id="app">
    			<my-fristcomponent></my-fristcomponent>
    		</div>
    
    		<div id="app1">
    			<!-- 在视图层 直接写组件的标签名就可以调用全局组件 -->
    			<my-fristcomponent></my-fristcomponent>
    		</div>
    		<!-- 
    			值得注意的点是 在js中定义了Vue全局组件在视图层中我们的全局组件必须在创建实例的块中使用
    		 -->
    		<!-- 如下,app2我们没有绑定vue实例所以组件不会生效-->
    		<div id="app2">
    			<my-fristcomponent></my-fristcomponent>
    		</div>
    		<my-fristcomponent></my-fristcomponent>
    
    		<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
    		<script type="text/javascript">
    			/* 
    				自定义全局组件  my-fristcomponent是自定义组件的标签名
    				template是模板的意思,后面写组件的内容
    			 */
    			Vue.component('my-fristcomponent', {
    				template: "<p>我的第一个组件</p>"
    			});
    			/* 
    				创建 两个vue实例
    			 */
    			new Vue({
    				el: "#app"
    			});
    			new Vue({
    				el: "#app1"
    			})
    		</script>
    	</body>
    

    2.组件驼峰命名

    <body>
    		<div id="app">
    			<!-- 
    				在使用组件,如果使用驼峰命名,vue会自动解析,我们
    				调用时需要用-隔开并使用小写,如下两种方式是可以
    			 -->
    			<Hello-world></Hello-world>
    			<hello-world></hello-world>
    			<!-- 错误 -->
    			<HelloWorld></HelloWorld>
    		</div>
    		<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
    		<script type="text/javascript">
    			/* 在创建实例时,我们遵循驼峰命名法 */
    			Vue.component('HelloWorld', {
    				template: "<p>HelloWorld</p>"
    			})
    
    			new Vue({
    				el: "#app",
    			})
    		</script>
    	</body>
    

    3.组件data数据

    <body>
    		<div id="app">
    			<data-component></data-component>
    			<button @click="change">改变</button>
    			{{msg}}
    		</div>
    		<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
    		<script type="text/javascript">
    			Vue.component("dataComponent", {
    				/* 
    					在定义组件时也可以写data,但是data里面必须是一个函数
    					且这个函数必须有返回值
    				*/
    				data: function() {
    					return {
    						msg: "data返回值"
    					}
    				},
    				// 组件中的msg 只可以在该模板中使用
    				template: '<p >{{msg}}</p>'
    			})
    
    			new Vue({
    				el: "#app",
    				data: {
    					msg: "121"
    				}, 
    				methods: {
    					// 定义change返回无法改变组件中的
    					change: function() {
    						// alert(11);
    						this.msg = "1212";
    					}
    				}
    			})
    		</script>
    	</body>
    

    4.局部组件

    前面已经写个全局指令,全局过滤器,局部过滤器之类的,所以局部组件与全局组件的区别是一样的,局部组件只能在当前实例才能使用

    <body>
    		<div id="app">
    			<my-component></my-component>
    		</div>
    		<div id="app1">
    			// my-component组件注册不正确 局部组件不能在其他实例中使用
    			<my-component></my-component> 
    		</div>
    		<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
    		<script type="text/javascript">
    			var vm = new Vue({
    				el: "#app",
    			/* 	data: {
    					msg: "局部指令",
    				}, */
    				components:{
    					'my-component':{
    						data:function(){
    							return{
    								msg:"局部指令"
    							}
    						},
    						template:"<p>{{msg}}</p>"
    					}
    				}
    			});
    				new Vue({
    					el:"#app1"
    				})
    		</script>
    	</body>
    

    注意:组件模板中的内容只允许存在一个根标签,多了的话只会解析第一个

    	// 下面模板定义了两个p标签,相当于两个根标签,那么第二个就不会生效
    	template: '<p >{{msg}}</p> <p>{{msg}}</p>
    

    三.组件之间的传值

    1.父组件向子组件传值

    • 父组件发送的形式是以属性的形式绑定值到子组件身上。
    • 然后子组件用属性props接收
    • 在props中使用驼峰形式,模板中需要使用短横线的形式字符串形式的模板中没有这个限制
    <body>
    		<div id="app">
    			<p>实例中定义的属性内容是: {{pmsg}}</p>
    			#### 下面是组件
    			<!-- 
    				:content 等同于v-bind:content
    				浏览器打印结果:
    				我是定义的组件内容
    				我是标题
    				父组件内容
    			 -->
    			<!-- 
    				组件定义的模板中使用的值带有props的属性,在视图层使用v-bind绑定
    				父组件的数据从而形成赋值,也可以不绑定直接赋值
    				eg:<menu-item title="我自动赋值" content="我自动赋值内容"></menu-item>
    			 -->
    			<menu-item v-bind:title="ptitle" :content="pmsg"></menu-item>
    		</div>
    		<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
    		<script type="text/javascript">
    			// menu-item 定义全局组件
    			Vue.component('menu-item', {
    				// 组件 用props 属性来接受父组件传递的参数
    				props: ['title', 'content'],
    				//组件中 的data必须是函数,且有返回值
    				data: function() {
    					return {
    						msg: "我是定义的组件内容",
    						title: "我是定义的组件的标题"
    					}
    				},
    				// 定义组件模版
    				template: '<p><span>{{msg}}</span><br><span>{{title}}</span><br><span>{{content}}</span></p>'
    			});
    			new Vue({
    				el: "#app",
    				data: {
    					pmsg: "父组件内容",
    					ptitle: "我是标题"
    				}
    			});
    		</script>
    	</body>
    

    2.子组件向父组件传值

    • 子组件用$emit()触发事件
    • $emit() 第一个参数为 自定义的事件名称 第二个参数为需要传递的数据
    • 父组件用v-on 监听子组件的事件
    <body>
    		<div id="app">
    			<div :style='{fontSize: fontSize + "px"}'>{{pmsg}}</div>
    			<!-- 2 父组件用v-on 监听子组件的事件
    				这里 enlarge-text  是从 $emit 中的第一个参数对应   handle 为对应的事件处理函数	
    			-->
    			<menu-item :parr='parr' @enlarge-text='handle($event)' @shrink-text='shrink($event)'
    				@gain-num="gainNume($event)"
    			></menu-item>
    		</div>
    		<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
    		<script type="text/javascript">
    			/*
    		      子组件向父组件传值-携带参数
    		    */
    
    			Vue.component('menu-item', {
    				props: ['parr'],
    	/* 			data: function() {
    					return {
    						num: 0,
    					}
    				}, */
    				template: `
    		        <div>
    		          <ul>
    		            <li :key='index' v-for='(item,index) in parr'>{{item}}</li>
    		          </ul>
    					###  1、子组件用$emit()触发事件
    					### 第一个参数为 自定义的事件名称   第二个参数为需要传递的数据  
    					<br>
    				  <button @click='$emit("gain-num", 0)'>将子组件的值传递到父组件</button>
    		          <button @click='$emit("enlarge-text", 5)'>扩大父组件中字体大小</button>
    		          <button @click='$emit("shrink-text", 5)'>缩小父组件中字体大小</button>
    		        </div>
    		      `
    			});
    			var vm = new Vue({
    				el: '#app',
    				data: {
    					pmsg: '父组件中内容',
    					parr: ['apple', 'orange', 'banana'],
    					fontSize: 10
    				},
    				methods: {
    					handle: function(val) {
    						alert(val)
    						// 扩大字体大小
    						this.fontSize += val;
    					},
    					shrink: function(val) {
    						// 缩小 字体大小
    						this.fontSize -= val;
    					},
    					gainNume: function(val){
    						alert(val);
    					}
    				}
    			});
    		</script>
    	</body>
    

    3.兄弟组件的传递

    • 兄弟之间传递数据需要借助于事件中心,通过事件中心传递数据
      • 提供事件中心 var hub = new Vue()
    • 传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据)
    • 接收数据方,通过mounted(){} 钩子中 触发hub.$on()方法名
    • 销毁事件 通过hub.$off()方法名销毁之后无法进行传递数据

    ​ 简单想象一下兄弟组件,在全局组件中,定义两个全局组件那么这两个组件就是兄弟组件,局部组件也是一样的,在当个实例中可定义多个局部组件组件之间就是兄弟关系

    <body>
    		<div id="app">
    			<div>父组件</div>
    			<div>
    				<button @click='handle'>销毁事件</button>
    			</div>
    			<test-tom></test-tom>
    			<test-jerry></test-jerry>
    		</div>
    		<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
    		<script type="text/javascript">
    			/*
    		      兄弟组件之间数据传递
    		    */
    			//1、 提供事件中心
    			var hub = new Vue();
    
    			Vue.component('test-tom', {
    				data: function() {
    					return {
    						num: 0
    					}
    				},
    				template: `
    		        <div>
    		          <div>TOM:{{num}}</div>
    		          <div>
    		            <button @click='handle'>点击</button>
    		          </div>
    		        </div>
    		      `,
    				methods: {
    					handle: function() {
    						//2、传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据)   触发兄弟组件的事件
    						hub.$emit('jerry-event', 2);
    					}
    				},
    				mounted: function() {
    					// 3、接收数据方,通过mounted(){} 钩子中  触发hub.$on(方法名
    					hub.$on('tom-event', (val) => {
    						this.num += val;
    					});
    				}
    			});
    			Vue.component('test-jerry', {
    				data: function() {
    					return {
    						num: 0
    					}
    				},
    				template: `
    		        <div>
    		          <div>JERRY:{{num}}</div>
    		          <div>
    		            <button @click='handle'>点击</button>
    		          </div>
    		        </div>
    		      `,
    				methods: {
    					handle: function() {
    						//2、传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据)   触发兄弟组件的事件
    						hub.$emit('tom-event', 1);
    					}
    				},
    				mounted: function() {
    					// 3、接收数据方,通过mounted(){} 钩子中  触发hub.$on()方法名
    					hub.$on('jerry-event', (val) => {
    						this.num += val;
    					});
    				}
    			});
    			
    			var vm = new Vue({
    				el: '#app',
    				data: {
    
    				},
    				methods: {
    					handle: function() {
    						//4、销毁事件 通过hub.$off()方法名销毁之后无法进行传递数据  
    						hub.$off('tom-event');
    						hub.$off('jerry-event');
    					}
    				}
    			});
    		</script>
    	</body>
    

    4.自定义轮播组件

    四.组件插槽

    • 组件的最大特性就是复用性,而用好插槽能大大提高组件的可复用能力
    • 插槽标签指令
    • 在组件的基础上的一个标签

    1.匿名插槽

    • 没有被name属性修饰的插槽叫匿名插槽
    <body>
    		<div id="app">
    			<alert-box></alert-box>
    			<!-- 插槽相当于一个默认的占位符,如果没有去修饰值修饰它,它会以默认值展现 -->
    			<alert-box1>{{msg}}</alert-box1>
    		</div>
    
    		<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
    		<script type="text/javascript">
    			Vue.component("alert-box", {
    				template: `
    					<div>
    						###在组件模板中定一个插槽<br>
    						<slot>我是一个插槽</slot>
    					</div>
    				`
    			})
    			Vue.component("alert-box1", {
    				template: `
    					<div>
    						###虽然我也是一个插槽但是我被占用了<br>
    						<slot>我是一个插槽</slot>
    					</div>
    				`
    			})
    
    			new Vue({
    				el: "#app",
    				data: {
    					msg:"我要占用插槽"
    				}
    			});
    		</script>
    	</body>
    

    2.具名插槽

    • 被name属性修饰的插槽叫具名插槽
    <body>
    		<div id="app">
    			<table-list>
    				<!-- 在模板内分别为两个插槽填充数据 达到表格效果 -->
    				<template slot='heands'>
    					<th>ID</th>
    					<th>名称</th>
    				</template>
    				<template slot='tablebody'>
    					<tr v-for="l in list">
    						<td>{{l.id}}</td>
    						<td>{{l.name}}</td>
    					</tr>
    				</template>
    			</table-list>
    		</div>
    		<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
    		<script type="text/javascript">
    			// 定义一个 table 组件 
    			Vue.component('table-list', {
    				// 定义一个模板,定义两个插槽heands 和 tablebody
    				template: `
    					<table>
    						<tr>
    						   <slot name='heands'></slot> 
    						</tr>
    						<tbody>
    							 <slot name="tablebody"></slot> 
    						</tbody>
    					</table>
    				`
    			})
    			new Vue({
    				el: "#app",
    				data: {
    					// 准备初始化数据
    					list: [{
    							id: 1,
    							name: '李四'
    						},
    						{
    							id: 2,
    							name: '张三'
    						},
    						{
    							id: 3,
    							name: '张飞'
    						},
    					]
    				}
    			})
    		</script>
    	</body>
    

    3.作用域插槽

    • 父组件对子组件加工处理
    • 既可以复用子组件的slot,又可以使slot内容不一致
    <body>
    		<div id="app">
    			<!-- 
    				1、当我们希望li 的样式由外部使用组件的地方定义,因为可能有多种地方要使用该组件,
    				但样式希望不一样 这个时候我们需要使用作用域插槽 
    				
    			-->
    			<fruit-list :list='list'>
    				<!-- 2、 父组件中使用了<template>元素,而且包含scope="slotProps",
    					slotProps在这里只是临时变量   
    				--->
    				<template slot-scope='slotProps'>
    					<strong v-if='slotProps.info.id==3' class="current">
    						{{slotProps.info.name}}
    					</strong>
    					<span v-else>{{slotProps.info.name}}</span>
    				</template>
    			</fruit-list>
    		</div>
    		<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
    		<script type="text/javascript">
    			/*
    		      作用域插槽
    		    */
    			Vue.component('fruit-list', {
    				props: ['list'],
    				/* 
    				   3、 在子组件模板中,<slot>元素上有一个类似props传递数据给组件的写法msg="xxx",
    				   插槽可以提供一个默认内容,如果如果父组件没有为这个插槽提供了内容,会显示默认的内容。
    				 		如果父组件为这个插槽提供了内容,则默认的内容会被替换掉
    				 */
    				template: `
    		        <div>
    		          <li :key='item.id' v-for='item in list'>
    		            <slot :info='item'>{{item.name}}</slot>
    		          </li>
    		        </div>
    		      `
    			});
    			var vm = new Vue({
    				el: '#app',
    				data: {
    					list: [{
    						id: 1,
    						name: 'apple'
    					}, {
    						id: 2,
    						name: 'orange'
    					}, {
    						id: 3,
    						name: 'banana'
    					}]
    				}
    			});
    		</script>
    	</body>
    

    五.习题练习

    1.定义轮播组件

    <html>
    	<head>
    		<meta charset="utf-8">
    		<title>自定义轮播组件</title>
    	</head>
    	<style type="text/css">
    		* {
    			padding: 0;
    			margin: 0;
    		}
    
    		#slideshow {
    			 800px;
    			height: 400px;
    			margin: 50px auto;
    			border: 1px solid black;
    			position: relative;
    		}
    	</style>
    	<body>
    		<div id="slideshow">
    			<slides-show :images="images" :index="index" :img-style="imgStyle" :show="show" :hide="hide" :prepagestye="prepagestye"
    			 :nextpagestye="nextpagestye" :slideshowstyle="slideshowstyle"
    			 @prepage='prepage' @nextpage='nextpage'
    			 >
    
    			</slides-show>
    		</div>
    
    		<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
    		<script type="text/javascript">
    			Vue.component("slides-show", {
    				props: ['images', 'index', 'slideshowstyle', 'imgStyle', 'show', 'hide',
    					'prepagestye', 'nextpagestye'
    				],
    				template: `
    				<div :style="[slideshowstyle]">
    					<ul v-for="imgs in images" class="slideTable">
    						<li v-if="index === imgs.id" :style="[show]">
    							<img :src="imgs.path" :style="[imgStyle]">
    						</li>
    						<li v-else :style="[hide]">
    							<img :src="imgs.path" >
    						</li>
    					</ul>
    					<span :style="[prepagestye]" @click='$emit("prepage")'>
    						<img src = "img/上一页.png" width="30px">
    					</span>
    					<span :style="[nextpagestye]"  @click='$emit("nextpage")'>
    						<img src = "img/上一页.png" width="30px">
    					</span>
    				</div>
    				`
    			})
    			new Vue({
    				// 绑定父元素
    				el: "#slideshow",
    				data: {
    					// 定义轮播图片数据
    					index: 1,
    					images: [{
    							id: 1,
    							path: 'img/3Dbg01.jpg'
    						},
    						{
    							id: 2,
    							path: 'img/3Dbg02.jpg'
    						},
    						{
    							id: 3,
    							path: 'img/3Dbg03.jpg'
    						},
    						{
    							id: 4,
    							path: 'img/3Dbg04.jpg'
    						}
    					],
    					// 隐藏显现
    					hide: {
    						display: 'none'
    					},
    					show: {
    						display: 'inline'
    					},
    					//定义图片大小
    					imgStyle: {
    						 '100%',
    						height: '400px'
    					},
    					// 上一页
    					prepagestye: {
    						 '35px',
    						position: 'absolute',
    						top: '190px',
    						display: 'inline-block',
    						left: '8px',
    						cursor: 'pointer',
    					},
    					// 下一页
    					nextpagestye: {
    						 '35px',
    						position: 'absolute',
    						top: '190px',
    						right: '8px',
    						cursor: 'pointer',
    						display: 'inline-block',
    						transform: ' rotate(180deg)',
    					},
    					slideshowstyle: {
    						position: 'relative',
    						 '100%',
    						height: '400px'
    					}
    				},
    				methods: {
    					prepage: function() {
    						console.log('11')
    						if(this.index <= 1){
    							this.index = this.images.length;
    						}else{
    							this.index = this.index-1;
    						}
    					},
    					nextpage: function() {
    						console.log('2')
    						if (this.index >= this.images.length) {
    							this.index = 1;
    						} else {
    							this.index = this.index + 1;
    						}
    					}
    				}
    			})
    		</script>
    		<!-- <style type="text/css">
    			.slideshowStyele {
    				position: relative;
    				text-align: center;
    				background-color: #fff;
    				list-style: none;
    				 100%;
    				height: auto;
    				 30px;
    				height: 30px;
    				transform: rotate(180deg);
    				cursor: pointer;
    				display: inline-block;
    			}
    		</style> -->
    	</body>
    </html>
    

    2.dmoe下载

    https://gitee.com/li_shang_shan/vue-component-learning-dmeo

  • 相关阅读:
    大数据的起步:初学者
    接触区块链
    学习开始了,博客开始了
    hadoop分布式的搭建过程
    Java笔试题
    JavaSCript全局变量与局部变量
    OSGI
    restful
    jersey
    JSP+Servlet+Mybits小例子
  • 原文地址:https://www.cnblogs.com/2979100039-qq-con/p/14070719.html
Copyright © 2020-2023  润新知