44.vue的组件

目录

一、什么是组件化

二、组件的基本使用

1.自己自定义一个标签

2.全局组件和局部组件

3.父组件和子组件

4.组件语法糖注册

5.组件模板抽离写法

6.组件数据的存放

7.父子组件的通信(父传子)

8.父子组件的通信(子传父)

9.父子的双向绑定

10.父组件访问子组件

11.子组件访问父组件(不建议使用)

三、插槽的使用

1.插槽的基本使用

2.具名插槽

3.作用域插槽


代码放到码云上了,可以自己拉取:https://gitee.com/cxy-xupeng/vue-test.git

一、什么是组件化

有的复杂问题太难解决,我们把复杂问题拆分成多个简单问题,然后解决每个简单问题,都解决好了再整合成一个(有点类似于写多个接口,然后业务里面运用各种接口形成不同的逻辑功能)

组件使用的三个步骤:

  1. 创建组件构造器
  2. 注册组件
  3. 使用组件

二、组件的基本使用

1.自己自定义一个标签

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<div id="app">
			<my-cpn></my-cpn>
			<my-cpn></my-cpn>
			<my-cpn></my-cpn>
		</div>
		
		<script src="../js/vue.js"></script>
		<script>
			//1.创建组件构造器对象
			const cpnc = Vue.extend({
				template:`
					<div>
						<h2>标题</h2>
						<p>内容,哈哈</p>
						<p>内容,呵呵</p>
					</div>
					`
			})
			
			//2.注册组件
			Vue.component('my-cpn',cpnc)
			
			
			const app = new Vue({
				el:'#app',
				data:{
					message:'你好,徐鹏'
				}
			})
		</script>
	</body>
</html>

2.全局组件和局部组件

上述是全局组件,怎么注册局部组件?

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<div id="app">
			<my-cpn></my-cpn>
			<my-cpn></my-cpn>
			<my-cpn></my-cpn>
			<xupeng></xupeng>
		</div>
		
		<script src="../js/vue.js"></script>
		<script>
			//1.创建组件构造器对象
			const cpnc = Vue.extend({
				template:`
					<div>
						<h2>标题</h2>
						<p>内容,哈哈</p>
						<p>内容,呵呵</p>
					</div>
					`
			})
			
			//2.注册组件(全局组件,意味着可以在多个VUE的实例下面使用)
			Vue.component('my-cpn',cpnc)
			
			
			
			/* 注册局部组件 */
			const app = new Vue({
				el:'#app',
				data:{
					message:'你好,徐鹏'
				},
				components:{
					//xupeng:使用组件时的标签名,cpnc:组件构造器
					xupeng:cpnc
				}
			})
		</script>
	</body>
</html>

3.父组件和子组件

先创建一个组件1,在创建一个组件2,吧组件1注册在组件2。那么2就是1的父亲。我们把组件2注册在Vue,在使用2的时候就会自动用到组件1。

但是因为只有组件2在Vue中注册,所以vue只认识组件2,并不认识组件1,你直接调用组件1不行,只能通过组件2调用组件1(你士兵的士兵不是你的士兵)

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<div id="app">
			<xupeng2></xupeng2>
		</div>
		
		<script src="../js/vue.js"></script>
		<script>
			//1.创建第一个组件构造器(子组件)
			const cpnc1 = Vue.extend({
				template:`
					<div>
						<h2>徐鹏1</h2>
						<p>内容,哈哈</p>
						<p>内容,呵呵</p>
					</div>
					`
			})
			
			//2.创建第二个组件构造器(父组件)
			/* 在第二个组件构造器中注册并使用第一个组件构造器 */
			const cpnc2 = Vue.extend({
				template:`
					<div>
						<h2>徐鹏2</h2>
						<p>内容,哈哈</p>
						<p>内容,呵呵</p>
						<xupeng1></xupeng1>
					</div>
					`,
					components:{
						xupeng1:cpnc1
					}
			})
			
			/* 注册局部组件 */
			const app = new Vue({
				el:'#app',
				data:{
					message:'你好,徐鹏'
				},
				components:{
					xupeng2:cpnc2
				}
			})
		</script>
	</body>
</html>

4.组件语法糖注册

全局组件,局部组件的语法糖如下:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<div id="app">
			<xupeng></xupeng>
			<xupeng2></xupeng2>
		</div>
		
		
		<script src="../js/vue.js"></script>
		<script>
			//全局组件:将创建构造器+注册组件融合到了一起
			Vue.component('xupeng',{
				template:`
					<div>
						<h2>徐鹏1</h2>
						<p>内容,哈哈</p>
						<p>内容,呵呵</p>
					</div>
					`
			})
			
			//局部组件
			const app = new Vue({
				el:'#app',
				data:{
					message:'你好,徐鹏'
				},
				components:{
					'xupeng2':{
						template:`
						<div>
							<h2>徐鹏2</h2>
							<p>内容,哈哈</p>
							<p>内容,呵呵</p>
						</div>
						`
					}
				}
			})
		</script>
	</body>
</html>

5.组件模板抽离写法

大家看了语法糖不知道有没有这个感觉:这语法糖用了反而更乱了。。。反正我是这个感觉

仔细看一下,语法糖减少了Vue.entend的过程,我们觉得乱是因为html写在里面了。所以需要想办法抽离html:

这里有两种方法,我们推荐第二种

(1)通过<script type="text/x-template">写文本

(2)通过<template>写文本

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<!-- 1.通过<script type="text/x-template">将文本写在这里 -->
		<script type="text/x-template" id='xupeng1'>
			<div>
				<h2>{
   
   {title}}</h2>
				<p>哈哈</p>
			</div>
		</script>
		<!-- 2.通过<template>将文本写在这里 -->
		<template id='xupeng2'>
			<div>
				<h2>徐鹏2</h2>
				<p>呵呵</p>
			</div>
		</template>
		
		
		<div id="app">
			<xupeng1></xupeng1>
			<xupeng2></xupeng2>
		</div>
		
		
		<script src="../js/vue.js"></script>
		<script>
			Vue.component('xupeng1',{
				template:'#xupeng1',
				data(){
						return{
							title:'徐鹏1'
						}
					}
			})
			
			Vue.component('xupeng2',{
				template:'#xupeng2'
			})
			
			
			//局部组件
			const app = new Vue({
				el:'#app',
				data:{
					message:'你好,徐鹏'
				}
			})
		</script>
	</body>
</html>

6.组件数据的存放

注:组件内部不能访问Vue实例属性!

你组件可以有html,为什么会没有data,还要用vue实例的data?那么多组件谁都来用?而且那么多组件重名怎么办?所以组件内部不能使用vue实例。

注:组件内部的data跟vue的不同,组件的data是一个函数,他的返回值才是我们真正要的数据

理解:为什么组件中的data必须是函数?

一个页面如果调用多个一样的组件,那么多次调用,我们肯定希望他们每次都独立数据,而不希望调用三套,缺却共用一套数据。

这里就涉及到了块级作用域的概念。在ES5里,只有函数(function)有块级作用域,ES6里新增了let。let中的if和for也是有块级作用域的概念的

因此,为了确保数据独立性,我们组件里的数据只能使用函数的方式

但是你如果说,我调用多个组件,就希望他们共用一套数据咋办?(纯属杠精问题)

定义一个全局变量,每次返回操作这个全局变量。虽然你每个组件互相隔离,但是如果都跟同一个变量发生关系,间接的所有组件的数据不是都有关系了?

7.父子组件的通信(父传子)

这一块东西稍微有点多,但是有后端基础,仔细看其实也还好。

父=>子:通过props向子组件传递消息(注:不支持驼峰)

父=>子实例1:直接传一个数组

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<div id="app">
			<xupeng1 :smovies='movies'></xupeng1>
		</div>
		
		<template id="xupeng1">
			<div>
				<ul>
					<li v-for="item in smovies">{
   
   {item}}</li>
				</ul>
				{
   
   {smovies}}
			</div>
		</template>
		
		<script src="../js/vue.js"></script>
		<script>
			/* 父传子:props */
			const xupeng1 = {
				template:'#xupeng1',
				props:['smovies'],
				data(){
					return{}
				}
			}
			
			
			const app = new Vue({
				el:'#app',
				data:{
					message:'你好,徐鹏',
					movies:['海贼王','火影忍者','游戏王','名侦探柯南']
				},
				components:{
					//增强写法,等价于xupeng1:xupeng1
					xupeng1
				}
			})
		</script>
	</body>
</html>

父=>子实例2:类型限制

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<div id="app">
			<xupeng1 :smovies='movies'></xupeng1>
		</div>
		
		<template id="xupeng1">
			<div>
				<ul>
					<li v-for="item in smovies">{
   
   {item}}</li>
				</ul>
				{
   
   {smovies}}
			</div>
		</template>
		
		<script src="../js/vue.js"></script>
		<script>
			/* 父传子:props */
			const xupeng1 = {
				template:'#xupeng1',
				props:{
					smovies:Array
				},
				data(){
					return{}
				}
			}
			
			
			const app = new Vue({
				el:'#app',
				data:{
					message:'你好,徐鹏',
					movies:['海贼王','火影忍者','游戏王','名侦探柯南']
				},
				components:{
					//增强写法,等价于xupeng1:xupeng1
					xupeng1
				}
			})
		</script>
	</body>
</html>

父=>子实例3:类型限制+提供默认值

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<div id="app">
			<xupeng1 :smovies='movies' :smessage='message'></xupeng1>
		</div>
		
		<template id="xupeng1">
			<div>
				<ul>
					<li v-for="item in smovies">{
   
   {item}}</li>
				</ul>
				{
   
   {smovies}}
				<br /><br />
				{
   
   {smessage}}
			</div>
		</template>
		
		<script src="../js/vue.js"></script>
		<script>
			/* 父传子:props */
			const xupeng1 = {
				template:'#xupeng1',
				props:{
					/* 高版本,类型是对象或者数组时,默认值必须要是一个函数 */
					smovies:{
						type:Array,
						// default:['默认数组']  低版本可以这样写
						
						//高版本这样写
						default(){
							return ['默认数组']
						}
						
					},
					smessage:{
						type:String,
						default:'默认字符串',
						required:true
					}
					
				},
				data(){
					return{}
				}
			}
			
			
			const app = new Vue({
				el:'#app',
				data:{
					message:'你好,徐鹏',
					movies:['海贼王','火影忍者','游戏王','名侦探柯南']
				},
				components:{
					//增强写法,等价于xupeng1:xupeng1
					xupeng1
				}
			})
		</script>
	</body>
</html>

父=>子实例4:怎么处理驼峰命名

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<div id="app">
			<xupeng1 :smovies='movies' :s-message='message'></xupeng1>
		</div>
		
		<template id="xupeng1">
			<div>
				<ul>
					<li v-for="item in smovies">{
   
   {item}}</li>
				</ul>
				{
   
   {smovies}}
				<br /><br />
				{
   
   {sMessage}}
			</div>
		</template>
		
		<script src="../js/vue.js"></script>
		<script>
			/* 父传子:props */
			const xupeng1 = {
				template:'#xupeng1',
				props:{
					/* 高版本,类型是对象或者数组时,默认值必须要是一个函数 */
					smovies:{
						type:Array,
						// default:['默认数组']  低版本可以这样写
						
						//高版本这样写
						default(){
							return ['默认数组']
						}
						
					},
					sMessage:{
						type:String,
						default:'默认字符串',
						required:true
					}
					
				},
				data(){
					return{}
				}
			}
			
			
			const app = new Vue({
				el:'#app',
				data:{
					message:'你好,徐鹏',
					movies:['海贼王','火影忍者','游戏王','名侦探柯南']
				},
				components:{
					//增强写法,等价于xupeng1:xupeng1
					xupeng1
				}
			})
		</script>
	</body>
</html>

你看,我们直接驼峰是会报错的,但是改成这样就可以了:

主要改的是,父和子关联的时候,需要把驼峰变成“-”的形式

8.父子组件的通信(子传父)

子=>父:通过事件向父组件发送消息

流程:

(1)在子组件中,通过$emit()来触发事件

(2)在父组件中,通过v-on(语法糖@)来监听子组件事件

实例:

你如果看到最后你会问,最后不还是进的父组件方法嘛,为什么要绕这么一大圈呢?

因为仔细看,通过子传父进入父组件方法的时候,会有一个item参数,这个参数是子组件传来的。我们绕了一大圈的目的其实就是为了这个参数

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<!-- 父组件模板 -->
		<div id="app">
			<cpn @itemclick='cpnClick'></cpn>
		</div>
		<!-- 子组件模板 -->
		<template id="cpn">
			<div>
				<button v-for="item in categories" @click="btnClick(item)">{
   
   {item.name}}</button>
			</div>
		</template>
		<script src="../js/vue.js"></script>
		<script>
			const cpn = {
				template:'#cpn',
				data(){
					return {
						categories:[
							{id:'a',name:'徐鹏1'},
							{id:'b',name:'徐鹏2'},
							{id:'c',name:'徐鹏3'},
							{id:'d',name:'徐鹏4'},
						]
					}
				},
				methods:{
					btnClick(item){
						/* 发射事件:自定义事件 */
						this.$emit('itemclick',item)
					}
				}
			}
			const app = new Vue({
				el:'#app',
				data:{
					message:'你好,徐鹏'
				},
				components:{
					cpn
				},
				methods:{
					cpnClick(item){
						console.log(item)
					}
				}
			})
		</script>
	</body>
</html>

9.父子的双向绑定

(1)v-model = :value + @input

(2)点击鼠标修改数字的时候,通过子传父传给父组件。父组件监听到这个子传父之后,自己做出修改

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<div id="app">
			<sonapp :xupengson1='xupengParent1' :xupengson2='xupengParent2'
					@xupengparent1change='xupengParent1Change' @xupengparent2change='xupengParent2Change'></sonapp>
					
			{
   
   {xupengParent1}}<br>
			{
   
   {xupengParent2}}
		</div>
		
		<template id="sonapp">
			<div>
				<h2>props:{
   
   {xupengson1}}</h2>
				<h2>data:{
   
   {sondatanumber1}}</h2>
				<input type="text" :value="sondatanumber1" @input="son1input" />
				<h2>props:{
   
   {xupengson2}}</h2>
				<h2>data:{
   
   {sondatanumber2}}</h2>
				<input type="text" :value="sondatanumber2" @input="son2input" />
			</div>
		</template>
		
		<script src="../js/vue.js"></script>
		<script>
			const app = new Vue({
				el:'#app',
				data:{
					xupengParent1:1,
					xupengParent2:2
				},
				methods:{
					xupengParent1Change(value){
						this.xupengParent1 = parseInt(value)
					},
					xupengParent2Change(value){
						this.xupengParent2 = parseInt(value)
					},
				},
				components:{
					sonapp:{
						template:"#sonapp",
						props:{
							xupengson1:Number,
							xupengson2:Number,
						},
						data(){
							return{
								sondatanumber1:this.xupengson1,
								sondatanumber2:this.xupengson2,
							}
						},
						methods:{
							son1input(event){
								this.sondatanumber1 = event.target.value;
								this.$emit('xupengparent1change',this.sondatanumber1)
							},
							son2input(event){
								this.sondatanumber2 = event.target.value;
								this.$emit('xupengparent2change',this.sondatanumber2)
							},
						}
					}
				}
			})
		</script>
	</body>
</html>

10.父组件访问子组件

前面我们讲了父子组件之间传输数据,但是父组件、子组件其实都是个对象。是个对象就可以访问这个对象。我们通过父组件访问子组件,拿到子组件对象之后也可以获取值。

父组件访问子组件有两种方式:

(1)$children:获取子标签对象,这种方法不推荐,因为顺序依赖数组下标

(2)$refs:在子标签上得加上ref属性

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<div id="app">
			<cpn></cpn>
			<cpn></cpn>
			<cpn ref='aaa'></cpn>
			<button @click="btnClick">按钮</button>
		</div>
		
		
		<template id='cpn'>
			<div>子组件</div>
		</template>
		
		<script src="../js/vue.js"></script>
		<script>
			const app = new Vue({
				el:'#app',
				data:{
					message:'你好,徐鹏'
				},
				methods:{
					btnClick(){
						/* 1.$children获取子标签对象,这种方法不推荐,因为顺序依赖数组下标 */
						// console.log(this.$children);
						// for(let c of this.$children){
						// 	console.log(c.name)
						// 	c.showMessage()
						// }
						
						/* 2.推荐使用$refs,但是在子标签上得加上ref属性 */
						console.log(this.$refs.aaa)
					}
				},
				components:{
					cpn:{
						template:'#cpn',
						data(){
							return{
								name:'子组件name'
							}
						},
						methods:{
							showMessage(){
								console.log('showMessage')
							}
						}
					}
				}
			})
		</script>
	</body>
</html>

11.子组件访问父组件(不建议使用)

this.$parent:访问父组件

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<div id="app">
			<cpn></cpn>
		</div>
		
		
		<template id='cpn'>
			<ccpn></ccpn>
		</template>
		
		<template id='ccpn'>
			<div>
				子子组件
				<button @click="btnClick">按钮</button>
			</div>
		</template>
		
		<script src="../js/vue.js"></script>
		<script>
			const app = new Vue({
				el:'#app',
				data:{
					message:'你好,徐鹏'
				},
				components:{
					cpn:{
						template:'#cpn',
						data(){
							return{
								name:'cpn的name'
							}
						},
						components:{
							ccpn:{
								template:'#ccpn',
								methods:{
									btnClick(){
										//1.访问父组件
										console.log(this.$parent)
										console.log(this.$parent.name)
										
										//2.访问根组件
										console.log(this.$root.message)
									}
								}
							}
						}
					}
				}
			})
		</script>
	</body>
</html>

三、插槽的使用

1.插槽的基本使用

<slot>标签,该标签可以有默认值。

如果模板里自定义了,就会替代标签内的默认值

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<div id="app">
			<xupeng></xupeng>
			<xupeng><span>哈哈</span></xupeng>
			<xupeng><i>呵呵</i><span>哈哈</span></xupeng>
		</div>
		
		<template id='xupeng'>
			<div>
				<h2>徐鹏</h2>
				<slot><button>按钮</button></slot>
			</div>
		</template>
		
		
		<script src="../js/vue.js"></script>
		<script>
			const app = new Vue({
				el:'#app',
				data:{
					message:'你好,徐鹏'
				},
				components:{
					xupeng:{
						template:'#xupeng'
					}
				}
			})
		</script>
	</body>
</html>

2.具名插槽

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<div id="app">
			<xupeng>
				<span slot="left">替换插槽</span>
				<button slot="center">替换按钮</button>
			</xupeng>
		</div>
		
		<template id='xupeng'>
			<div>
				<slot name="left"><span>左边</span></slot>
				<slot name="center"><span>中间</span></slot>
				<slot name="right"><span>右边</span></slot>
				<slot><span>哈哈</span></slot>
			</div>
		</template>
		
		
		<script src="../js/vue.js"></script>
		<script>
			const app = new Vue({
				el:'#app',
				data:{
					message:'你好,徐鹏'
				},
				components:{
					xupeng:{
						template:'#xupeng'
					}
				}
			})
		</script>
	</body>
</html>

3.作用域插槽

编译作用域:父组件模板的所有东西都会在父级作用域编译,子组件模板的所有东西都会在子级作用域编译

作用域插槽:父组件提供插槽的标签,但是内容由子组件来提供

实例:(插槽空间==>插槽数据)

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<div id="app">
			<xupeng></xupeng>
			
			<xupeng>
				<template slot-scope='xupengslot'>
					<!-- <span v-for="item in slot.data">{
   
   {item}}--</span> -->
					<span>{
   
   {xupengslot.xupeng.join('  -- ')}}</span>
				</template>
			</xupeng>
			
		</div>
		
		<template id='xupeng'>
			<div>
				<slot :xupeng="language">
					<ul>
						<li v-for="item in language">{
   
   {item}}</li>
					</ul>
				</slot>
			</div>
		</template>
		
		
		<script src="../js/vue.js"></script>
		<script>
			const app = new Vue({
				el:'#app',
				data:{
					message:'你好,徐鹏'
				},
				components:{
					xupeng:{
						template:'#xupeng',
						data(){
							return{
								language:['java','linux','python']
							}
						}
					}
				}
			})
		</script>
	</body>
</html>

猜你喜欢

转载自blog.csdn.net/qq_40594696/article/details/110134023
44