组件的构成

组件模块化是Vue的渐进式框架的特点,了解组件的构成是迈开模块化开发的第一步,组件分为全局组件局部组件

  • 全局组件:
<body>
	<div id="app">
		<cpn></cpn>
	</div>
</body>
`//全局组件
	const cpn1 =  Vue.extend({
		template:`<div>
					<h1>我是全局标题1</h1>
					<p>我是全局内容1</p>
				  </div>`,
	})`
	// 注册全局组件,可以全局使用
	Vue.component("cpn",cpn1)
  • 局部组件
<body>
	<div id="app">
		<cpn1></cpn1>
	</div>
</body>
const app = new Vue({
		el:"#app",
		// 局部组件
		components:{
			cpn1:{
				template:`<div>
					<h1>我是局部组件标题1</h1>
					<p>我是局部内容1</p>
				</div>`
			}
		},
	})

父子组件的区分

存在组件就会有组件的嵌套,就会上下级的关系,就是父组件,和子组件

//第一个组件构造器
const cpn1 =  Vue.extend({
	template:`<div>
				<h1>我是第一个标题</h1>
				<p>我是第一个内容</p>
			  </div>`,
})
//第二个组件构造器
const cpn2 =  Vue.extend({
	template:`<div>
				<h1>我是第二个标题</h1>
				<cpn1></cpn1>
				<p>我是第二个内容</p>
			  </div>`,
	components:{
		cpn1:cpn1
	}
})

父子组件的通信

  • 父传子(驼峰命名的问题,props属性直接传递)
  • 子传父(对象方式)
<!-- 
	父传子,通过props传递
	子传父,自定义事件
 -->
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>

	<!-- 父组件模板 -->
	<div id="app">
		<h1>父组件</h1>
		<Child :cmovies="movies" :cmessage="message" :cactors="actors"></Child>
		<Child2></Child2>
		<!-- 子传父  -->
		<Cpn @item-click="cpnClick"></Cpn>
	</div>
</body>

<!-- 子组件模板 -->
<template id="child">
	<div>
		<h2>父组件传值的子组件</h2>
		<p>父传子后cmessage:{{cmessage}}</p>
		<ul>
			<li v-for="item in cmovies">{{item}}</li>
		</ul>
		父传子后cactors:演员名
		<ul>
			<li v-for="items in cactors">{{items}}</li>
		</ul>
	</div>	
</template>

<!-- 子传父模板 -->
<template id="cpn">
	<div>
		<button v-for="item in Lists" 
		@click="cpnClick(item)"
		>{{item.name}}</button>
	</div>
</template>

<script>
const Cpn = {
	template:"#cpn",
	data(){
		return{
			Lists:[
				{id:0,name:'热门推荐'},
				{id:1,name:'手机数码'},
				{id:2,name:'电脑科技'},
				{id:3,name:'家电设备'},
			]
		}
	},
	methods:{
		cpnClick(item){
			// 子传父的方法,使用$emit('定义方法名')
			this.$emit("item-click",item)
		}
	}
}

//构建子组件
const Child = {
	template:'#child',
	// props:["cmovies","cmessage"],
	props:{
		cmovies:{
			type:Array,
			// 如果类型是数组或者对象,default必须是个函数,返回一个默认值
			default(){
				return["未上映电影!"]
			},
			// true:使用时值必须传值,不传就报错;fales:使用时可传可不传
			required:false
		},
		cmessage:{
			type:String,
			// 默认值(string)
			default:"你好!",
			// true:使用时值必须传值,不传就报错;fales:使用时可传可不传
			required:false
		},
		cactors:{
			type:Object,
			default(){
				return{

				}
			}
		}
	},
	// 这里的data必须是函数,且有返回值
	data(){
		return{

		}
	}
}

// 父组件
const app = new Vue({
	el:"#app",
	data:{
		message:'蜘蛛侠电影集',
		movies:["蜘蛛侠",'蜘蛛侠2','蜘蛛侠3','超凡蜘蛛侠','超凡蜘蛛侠2','蜘蛛侠:英雄归来-返校季','蜘蛛侠:英雄远征'],
		actors:{
			name:'托比马奎尔',
			name1:'荷兰弟'
		}
	},
	methods:{
		// 子传父方法
		cpnClick(item){
		// 父组件接收到子组件传值
			console.log( item.id,item.name )
		}
	},
	components:{
		Child,
		Cpn,
		Child2:{
			template:`<div>
						<h2>父组件不传值显示默认值的子组件2</h2>
						<p>父未传子显示默认值的bmessage:{{bmessage}}</p>
						<p>父未传子显示默认值的bmessage</p>
						<ul>
							<li v-for="item in bmovies">{{item}}</li>
						</ul>

						<ul>
							<li v-for="items in bactors">{{items}}</li>
						</ul>
					</div>	`,
			props:{
				bmovies:{
					type:Array,
					// 如果类型是数组或者对象,default必须是个函数,返回一个默认值
					default(){
						return["未上映电影!"]
					},
					// true:使用时值必须传值,不传就报错;fales:使用时可传可不传
					required:false
				},
				bmessage:{
					type:String,
					// 默认值(string)
					default:"你好!",
					// true:使用时值必须传值,不传就报错;fales:使用时可传可不传
					required:false
				},
				bactors:{
					type:Object,
					default(){
						return{
							name:'尚未演员0',
							name1:'尚未演员1'
						}
					}
				}
			}
		}
	}

})

</script>
</html>
  • 父子传递双向绑定案例
    基础普通写法
<!-- 
	需求:
	1.使用input的双向绑定实现修改子组件的值更改父组件的值
	2.在修改num时,num1的值为num值的100倍
	3.在修改num1时,num的值为num1的0.01倍
 -->
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
	<!-- 父组件模板 -->
	<div id="app">
		<h1>number:{{number}}</h1>
		<h1>number1:{{number1}}</h1>
		<cpn :cnumber="number" :cnumber1="number1" 
			@changenum="childNum" 
			@changenum1="childNum1"
		/>
	</div>
</body>

<!-- 子组件模板 -->
<template id="child">
	<div>

		<h1>Props-Number:{{cnumber}}</h1>
		<h1>data-Number:{{bnumber}}</h1>
		<!-- 使用v-model实现了在子组件中的双向绑定,但不能改变父组件的值,需要通过watch方法才可以实现 -->	
 		<input type="text" :value="bnumber" @Input="changeNum($event)"> 
 		
		<h1>Props-Number1:{{cnumber1}}</h1>
		<h1>data-Number1:{{bnumber1}}</h1>
		
		<input type="text" :value="bnumber1" @Input="changeNum1($event)">

	</div>	
</template>
<script>
	const app = new Vue({
		el:"#app",
		data:{
			number:1,
			number1:2
		},
		methods:{
			childNum(e){
				// console.log(e)一般过传过来的参数都是strin类型,所以需要转换一下
				this.number = parseFloat(e)
			},
			childNum1(e){
				// console.log(e)一般过传过来的参数都是strin类型,所以需要转换一下
				this.number1 = parseFloat(e)
			},
		},
		components:{
			// 在修改父组件传给子组件的值时,不要直接修改,要通过data或者computeds修改
			cpn:{
				template:"#child",
				props:{
					cnumber:{
						type:Number,
						default:'none',
					},
					cnumber1:{
						type:Number,
						default:'none',
					}

				},
				data(){
					return{
						bnumber:this.cnumber,
						bnumber1:this.cnumber1
					}
				},
				methods:{
					changeNum(event){
						// console.log( event.target.value )获取到input框的值
						this.bnumber = event.target.value;
						// 将子组件修改的值传给父组件中
						this.$emit( "changenum",this.bnumber )
						// 修改关联值,并再次传给父组件(我这里一开始写错了,又写一个方法,其实传第二个值直接用第二个方法就行了)
						this.bnumber1 = this.bnumber*100
						this.$emit( "changenum1",this.bnumber1 )
					},
					changeNum1(event){
						// console.log( event.target.value )获取到input框的值
						this.bnumber1 = event.target.value;
						// 将子组件修改的值传给父组件中(我这里一开始写错了,又写一个方法,其实传第一个值直接用第一个方法就行了)
						this.$emit( "changenum1",this.bnumber1 )
						// 修改关联值,并再次传给父组件
						this.bnumber = this.bnumber1/100
						this.$emit( "changenum",this.bnumber )
					}
				},
				// 组件的一个属性,可以省略通过改变子组件的data来传递给父组件
			}
		}
	})
</script>
</html>

使用watch属性写法:

<!-- 
	需求:
	1.使用input的双向绑定实现修改子组件的值更改父组件的值
	2.在修改num时,num1的值为num值的100倍
	3.在修改num1时,num的值为num1的0.01倍
 -->
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
	<!-- 父组件模板 -->
	<div id="app">
		<h1>number:{{number}}</h1>
		<h1>number1:{{number1}}</h1>
		<cpn :cnumber="number" :cnumber1="number1" 
			@changenum="childNum" 
			@changenum1="childNum1"
		/>
	</div>
</body>

<!-- 子组件模板 -->
<template id="child">
	<div>

		<h1>Props-Number:{{cnumber}}</h1>
		<h1>data-Number:{{bnumber}}</h1>
		<!-- 使用v-model实现了在子组件中的双向绑定,但不能改变父组件的值,需要通过watch方法才可以实现 -->
		<input type="text" v-model="bnumber">
		<h1>Props-Number1:{{cnumber1}}</h1>
		<h1>data-Number1:{{bnumber1}}</h1>
		<input type="text" v-model="bnumber1">
	</div>	
</template>
<script>
	const app = new Vue({
		el:"#app",
		data:{
			number:1,
			number1:2
		},
		methods:{
			childNum(e){
				// console.log(e)一般过传过来的参数都是strin类型,所以需要转换一下
				this.number = parseFloat(e)
			},
			childNum1(e){
				// console.log(e)一般过传过来的参数都是strin类型,所以需要转换一下
				this.number1 = parseFloat(e)
			},
		},
		components:{
			// 在修改父组件传给子组件的值时,不要直接修改,要通过data或者computeds修改
			cpn:{
				template:"#child",
				props:{
					cnumber:{
						type:Number,
						default:'none',
					},
					cnumber1:{
						type:Number,
						default:'none',
					}

				},
				data(){
					return{
						bnumber:this.cnumber,
						bnumber1:this.cnumber1
					}
				},
				methods:{
					changeNum(event){
						// console.log( event.target.value )获取到input框的值
						this.bnumber = event.target.value;
						// 将子组件修改的值传给父组件中
						this.$emit( "changenum",this.bnumber )
						// 修改关联值,并再次传给父组件(我这里一开始写错了,又写一个方法,其实传第二个值直接用第二个方法就行了)
						this.bnumber1 = this.bnumber*100
						this.$emit( "changenum1",this.bnumber1 )
					},
					changeNum1(event){
						// console.log( event.target.value )获取到input框的值
						this.bnumber1 = event.target.value;
						// 将子组件修改的值传给父组件中(我这里一开始写错了,又写一个方法,其实传第一个值直接用第一个方法就行了)
						this.$emit( "changenum1",this.bnumber1 )
						// 修改关联值,并再次传给父组件
						this.bnumber = this.bnumber1/100
						this.$emit( "changenum",this.bnumber )
					}
				},
				// 组件的一个属性,可以省略通过改变子组件的data来传递给父组件
				watch:{
					bnumber(newVaule){
						// console.log( "newVaule:"+newVaule )
						this.bnumber1 = newVaule*100
						this.$emit( "changenum1",this.bnumber1 )
					},
					bnumber1(newVaule){
						// console.log( "newVaule:"+newVaule )
						this.bnumber = this.bnumber1/100
						this.$emit( "changenum",this.bnumber )
					}
				}
			}
		}
	})
</script>
</html>
  • 父组件拿到子组件的方法($ children 或者$ refs ),或者子组件拿父组件的方法($ parent或者$ root),需要注意的细节都写到代码注释里。
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
	<div id="app">
		<cpn></cpn>
		<cpn></cpn>
		<cpn ref="getChild"></cpn>
		<button @click="btnClick">按钮</button>
		<h2>展示子组件的data值:{{childrenVaule}}</h2>
	</div>

	<template id="cpn">
		<h1>我是子组件</h1>
	</template>
</body>
<script>
	var app = new Vue({
		el:'#app',
		data:{
			childrenVaule:""
		},
		methods:{
			btnClick(){
				// 调取子组件方法  $children $refs,由于this.$children是一个数组,所以如果组件
				// 数量多,则不好选择,所以一般使用$refs方法,只用当需要拿到所有子组件的值的时候,才会
				// 使用$children的方法
				// console.log( this.$children )
				
				// 在父组件上调取了子组件的children方法
				// this.$children[0].showMessage()
				
				//在父组件上调取了子组件的refs方法(推荐使用)也可以拿到子组件的data值:
				console.log( this.$refs )
				this.$refs.getChild.showMessage()
				this.childrenVaule = this.$refs.getChild.name
			}
		},
		components:{
			cpn:{
				template:"#cpn",
				data(){
					return{
						name:"子组件的name"
					}
				},
				methods:{
					showMessage(){
						console.log( "showMessage" )
					}
				}
			}
			
		}
	})

	
</script>

</html>