Vue笔记:组件化(一)

Vue组件化开发入门

可以一个完整的页面分成很多个组件,每个组件实现一个功能模块,每一个组件又可以细分

一、Vue的组件化思想

Vue提供了一种抽象,可以开发复用的小组件来构造应用,每一个应用都可以被抽象成一颗组件树

二、注册组件的步骤

step 1:通过Vue.extend()创建组件构造器

step2 :注册组件Vue.component()注册组件

step3 :使用

三、组件简单实现:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>Document</title>
	</head>
	<body>
		<div id="app">
			<!-- 使用 -->
			<my-cpn></my-cpn>
		</div>
		<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
		<script>
			//1 创建组件构造器对象
			let cpnC=Vue.extend({
				template:`
					<div>
						<h2>组件</h2>
					</div>
				`
			})
			//2 注册组件,字后用什么标签使用组件
			Vue.component('my-cpn',cpnC)
			var app=new Vue({
				el:'#app',
				data:{},
				methods:{}
			})
			
		</script>
	</body>
</html>

在实现时碰到了一个问题:

vue.js:634 [Vue warn]: Unknown custom element: - did you register the component correctly? For recursive components, make sure to provide the “name” option.

这是因为第一次写这串代码时,组件名定义为my-Cpn

Vue.component('my-Cpn',cpnC)

使用时:

<!-- 使用 -->
			<my-Cpn></my-Cpn>

这个报错的问题在于html的标签是不区分大小写的,所以其实html读到的标签不是<my-Cpn>而是my-cpn,但是js代码区分大小写,所以会出现<my-cpn>标签未注册的问题,解决方式是在注册组件时不要出现大写字母,如果一定要定义大写字母,在html中使用时也要写成小写

vue.extend()

  • 调用Vue.extend()创建的是一个组件构造器
  • 通常在创建组件构造器的时候,传入template代表我们自定义组件的模板
  • 模板就是使用组件的地方要显示HTML的代码

vue.component()

调用Vue.component()是将刚才的组件构造器注册为一个组件,并且给它起了一个组件的标签名称,所以要传递两个参数:组件的标签名,组件构造器

四、全局组件和局部组件

在某个Vue实例里注册的组件时局部组件,在Vue实例外面注册的组件时全局组件

全局组件的注册如上所示,下面是局部组件的注册

<script>
    //1 创建组件构造器对象
    let cpnC=Vue.extend({
        template:`
        <div>
        <h2>组件</h2>
            </div>
        `
    })
    //2 注册组件,字后用什么标签使用组件,全局注册
    Vue.component('my-cpn',cpnC)
    var app=new Vue({
        el:'#app', 
        data:{},
        methods:{},
        components:{
            'cpn':cpnC
        }
    })
			
</script>

五、父组件和子组件

<script>
    //1 创建组件构造器对象
    let cpnC1=Vue.extend({
    template:`
        <div>
        <h2>组件1</h2>
        </div>
        `
    })
    const cpnC2=Vue.extend({
    template:`
        <div>
        <h2>组件2</h2>
        <cpn1></cpn1>
        </div>`
        ,
        components:{
        cpn1:cpnC1//子组件cpnC1在这里注册,cpn1只能在cpn2中用
        }
    })
    var app=new Vue({
        el:'#app', 
        data:{},
        methods:{},
        components:{
        	cpn2:cpnC2//cpnC2在这里注册,但是页面中可以显示cpnC1的内容
        }
    })

</script>

六、组件注册的语法糖

实际还是调用了Vue.extend,但是不用写出来

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>Document</title>
	</head>
	<body>
		<div id="app">
			<cpn1></cpn1>
			<cpn2></cpn2>
		</div>
		<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
		<script>
			//全局组件注册的语法糖
			Vue.component('cpn1',{
				template:`
					<div>
						<h2>组件1</h2>
					</div>
				`
			})
			
			var app=new Vue({
				el:'#app',
				data:{},
				methods:{},
				components:{
					'cpn2':{
				template:`
					<div>
					<h2>组件2</h2>
					</div>
				`}
				}
			})
		</script>
	</body>
</html>

七、模板的分离写法

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>Document</title>
	</head>
	<body>
		<div id="app">
			<cpn1></cpn1>
		</div>
		<!--方式1:用x-template-->
		<script type="text/x-template" id="cpn_1">
			<div>
				<h2>组件1</h2>
			</div>
		</script>
        <!--方式2:用template标签-->
		<template id="cpn_2">
			<div>
				<h2>组件2</h2>
			</div>
		</template>
		<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
		<script>
            //注册时用id
			Vue.component('cpn1',{
				template:'#cpn_2'
			})
			var app=new Vue({
				el:'#app',
				data:{},
				methods:{},
				components:{
					// cpn2:'#cpn_2'
				}
			})
		</script>
	</body>
</html>

八、组件访问Vue实例的数据

Vue的组件中不能访问Vue实例中的数据,每一个组件有自己的data属性,但是data属性不可以是对象,而要以函数的形式return一个对象,对象内保存了属性

同时在组件中也有自己的methods等其他属性

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>Document</title>
	</head>
	<body>
		<div id="app">
			<cpn1></cpn1>
		</div>
		
		<template id="cpn_1">
			<div>
				<h2>当前计数{{count}}</h2>
				<button type="button" @click="sub">-</button>
				<button type="button" @click="add">+</button>
			</div>
		</template>
		<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
		<script>
			Vue.component('cpn1',{
				template:'#cpn_1',
				data(){
					return{
						count:0
					}
				},
				methods:{
					add:function(){
						this.count++
					},
					sub:function(){
						this.count--
					}
				}
			})
			var app=new Vue({
				el:'#app',
				data:{},
				methods:{},
				components:{
				}
			})
		</script>
	</body>
</html>

8.1组件中的data为什么要是一个函数

data:{}这样写实际上是将data指向一个对象,data保存了这个对象的地址,组件在复用时是创建了一个组件的事例,但是如果实例中的data都指向了同一个对象地址,那么会造成更改一个事例里的数据,别的实例里的数据都被更改了。如果data(){}这样写,则是使用一个data函数,每一个实例都有自己的函数,自己return回去的对象,这样能够将实例之间的数据隔开,各用各的数据

九、父子组件之间的通讯

9.1通过props父组件向子组件传递数据

在这里插入图片描述

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>Document</title>
	</head>
	<body>
		<div id="app">
			<!-- 使用 -->
			<cpn1 :cmovies="movies"></cpn1><!--在实例化子组件时进行参数绑定-->
		</div>
		<template id="cpn1">
			<div>
				<h2>{{cmovies}}</h2><!--子组件的模板使用子组件中的数据-->
			</div>
		</template>
		<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
		<script>
			let cpn1={
				template:'#cpn1',
				props:['cmovies'],//在子组件中定义数据
				data(){
					return {}
				}
			}
			var app=new Vue({
				el:'#app', 
				data:{//父组件中的数据定义
					movies:["爱丽丝梦游仙境","网球王子","喜羊羊与灰太狼"]
				},
				methods:{},
				components:{
					cpn1//子组件在父组件中定义注册
				}
			})
			
		</script>
	</body>
</html>

多个组件套娃传递
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>Document</title>
	</head>
	<body>
		<div id="app">
			<!-- 使用 -->
			<cpn2 :fathermovies="movies"></cpn2>
		</div>
		<template id="cpn2">
			<div>
				<h2>父组件标题</h2>
				<div>
					<h2>子组件内容</h2>
					<cpn1 :sonMovies="fathermovies"></cpn1>
				</div>
			</div>
		</template>
		<template id="cpn1">
			<div>
				<h3>子组件标题</h3>
				<h4>{{sonmovies}}</h4>
			</div>
		</template>
		<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
		<script>
			let cpn1={
				template:'#cpn1',
				props:['sonmovies'],
				data(){
					return {}
				}
			}
			let cpn2={
				template:'#cpn2',
				props:['fathermovies'],
				data(){
					return {}
				},
				components:{
					cpn1:cpn1
				}
			}
			var app=new Vue({
				el:'#app', 
				data:{
					movies:["爱丽丝梦游仙境","网球王子","喜羊羊与灰太狼"]
				},
				methods:{},
				components:{
					cpn2
				}
			})
			
		</script>
	</body>
</html>

props支持哪些类型

也可以这样来写,这样写可以限制参数的类型和默认值

let cpn2={
				template:'#cpn2',
				props:{
					message:{
                        type:String,//限制类型
                    	default:["a","b"],//设置默认值
                        required:true//true表示这个值是必须的
                    }
				},
				data(){
					return {}
				},
				components:{
					cpn1:cpn1
				}
			}

type可以限定为多种,多个要用(type1,type2),自定义类型也可以用来验证,还可以用propF:{func:function(n){//验证函数}}来进行自己想要的验证

当props里的对象类型为array,那么deafult值不可以写成default:[],而要写成函数形式defualt(){return []}

验证支持的数据类型:String,Number,Boolean,Array,Object,Data,Function,Symbol

9.2自定义事件,从子组件向父组件传递数据

在这里插入图片描述

基本架构:
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>Document</title>
	</head>
	<body>
		<div id="app">
			<cpn></cpn>
		</div>
		<!-- 父组件模板 -->
		<template id="cpn">
			<div>
				<lpannel></lpannel>
			</div>
		</template>
		<!-- 子组件模板 -->
		<template id="leftPannel">
			<div>
				<button type="button" v-for="item in categories" @click="sendData(item)">{{item.name}}</button>
			</div>
		</template>
		<template id="rightPannel">
			<div>
				<h2>{{bookName}}</h2>
			</div>
		</template>
		<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
		<script>
			
			<!-- 子组件定义 -->
			const leftPannel={
				template:'#leftPannel',
				data(){
					return{
						categories:[
							{id:1,name:"源氏物语"},
							{id:2,name:"庄周"},
							{id:3,name:"霍乱时期的爱情"},
							{id:4,name:"简爱"},
							{id:5,name:"傲慢与偏见"}
						]
					}
				},
				methods:{
					sendData:function(item){
						//传递事件位置
					}
				}
			}
			const cpn={
				template:'#cpn',
				components:{
					'lpannel':leftPannel
				}
			}
			var app=new Vue({
				el:'#app',
				data:{},
				methods:{},
				components:{
					cpn
				}
			})
		</script>
	</body>
</html>

//传递事件位置写入要传递的事件和数据,使用this.$emit('事件名称',事件参数)

在子组件的注册函数中:
const leftPannel={
				template:'#leftPannel',
				data(){
					return{
						categories:[
							{id:1,name:"源氏物语"},
							{id:2,name:"庄周"},
							{id:3,name:"霍乱时期的爱情"},
							{id:4,name:"简爱"},
							{id:5,name:"傲慢与偏见"}
						]
					}
				},
				methods:{
					sendData:function(item){
						//发射事件
						console.log("send")
						this.$emit('itemclick',item)
					}
				}
			}
在父组件的模板中:
<template id="cpn">
			<div>
				<!-- 在父组件中监听事件 -->
				<lpannel @itemclick="cpnSendName"></lpannel>
			</div>
		</template>

在父组件的注册函数中:

const cpn={
				template:'#cpn',
				components:{
					'lpannel':leftPannel
				},
				methods:{
					// 父组件中接收后执行的函数
					cpnSendName:function(item){
						console.log(item)
					}
				}
			}
执行效果:

在这里插入图片描述

完整代码
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>Document</title>
	</head>
	<body>
		<div id="app">
			<cpn ></cpn>
		</div>
		<!-- 父组件模板 -->
		<template id="cpn">
			<div>
				<!-- 在父组件中监听事件 -->
				<lpannel @itemclick="cpnSendName"></lpannel>
			</div>
		</template>
		<!-- 子组件模板 -->
		<template id="leftPannel">
			<div>
				<button type="button" v-for="item in categories" @click="sendData(item)">{{item.name}}</button>
			</div>
		</template>
		<template id="rightPannel">
			<div>
				<h2>{{bookName}}</h2>
			</div>
		</template>
		<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
		<script>
			
			// 子组件注册函数
			const leftPannel={
				template:'#leftPannel',
				data(){
					return{
						categories:[
							{id:1,name:"源氏物语"},
							{id:2,name:"庄周"},
							{id:3,name:"霍乱时期的爱情"},
							{id:4,name:"简爱"},
							{id:5,name:"傲慢与偏见"}
						]
					}
				},
				methods:{
					sendData:function(item){
						//发射事件
						console.log("send")
						this.$emit('itemclick',item)
					}
				}
			}
			// 父组件注册函数
			const cpn={
				template:'#cpn',
				components:{
					'lpannel':leftPannel
				},
				methods:{
					// 父组件中接收后执行的函数
					cpnSendName:function(item){
						console.log(item)
					}
				}
			}
			var app=new Vue({
				el:'#app',
				data:{},
				methods:{
					
				},
				components:{
					cpn
				}
			})
		</script>
	</body>
</html>

物语"},
{id:2,name:“庄周”},
{id:3,name:“霍乱时期的爱情”},
{id:4,name:“简爱”},
{id:5,name:“傲慢与偏见”}
]
}
},
methods:{
sendData:function(item){
//发射事件
console.log(“send”)
this.$emit(‘itemclick’,item)
}
}
}
// 父组件注册函数
const cpn={
template:’#cpn’,
components:{
‘lpannel’:leftPannel
},
methods:{
// 父组件中接收后执行的函数
cpnSendName:function(item){
console.log(item)
}
}
}
var app=new Vue({
el:’#app’,
data:{},
methods:{

			},
			components:{
				cpn
			}
		})
	</script>
</body>
## 十、父子访问

10.1父访问子

$children

this.$children 是一个数组类型,它包含了所有子组件的对象

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>Document</title>
	</head>
	<body>
		<div id="app">
			<cpn></cpn>
			<button type="button" @click="btnClick">显示child</button>
		</div>
		<template id="cpn">
			<div id="">
				我是子组件
			</div>
		</template>
		<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
		<script>
			var app=new Vue({
				el:'#app',
				data:{},
				methods:{
					btnClick:function(){
						console.log(this.$children)
                        //children里会有很多子组件所以要用下标来访问某个子组件的数据
						this.$children[0].showMessage()
						console.log(this.$children[0].name)
					}
				},
				components:{
					cpn:{
						template:'#cpn',
						methods:{
							showMessage:function(){
								console.log("子组件showMessage")
							}
						},
						data(){
							return {
								name:"我是乔木"
							}
						}
					}
				}
			})
		</script>
	</body>
</html>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M4qkN2ce-1592214353129)(C:\Users\93221\AppData\Roaming\Typora\typora-user-images\image-20200615172143404.png)]

$refs

$refs是一个对象类型,默认是一个空对象

首先在子组件的实例化时定义一个ref属性,这个相当于子组件实例的一个key

<div id="app">
			<cpn ref="key"></cpn>
			<button type="button" @click="btnClick">显示child</button>
		</div>

在父组件访问这个实例时只用.$refs.key.name就可以访问子组件里的数据或函数

btnClick:function(){
						console.log(this.$refs)
						this.$refs.key.showMessage()
					}

效果

在这里插入图片描述

子访问父

$parent
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>Document</title>
	</head>
	<body>
		<div id="app">
			<cpn></cpn>
		</div>
		<template id="ccpn">
			<div>
				我是ccpn子组件
			<button type="button" @click="btnClick">访问父组件</button>
			</div>
		</template>
		<template id="cpn">
			<div>
				<h2>我是cpn</h2>
				<ccpn></ccpn>
			</div>
		</template>
		<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
		<script>
			var app=new Vue({
				el:'#app',
				data:{},
				methods:{
					
				},
				components:{
					cpn:{
						template:'#cpn',
						data(){
							return {
								name:"我是cpn组件的name"
							}
						},
						methods:{
							showMessage:function(){
								console.log("cpn组件的showMessage")
							}
						},
						components:{
							ccpn:{
								template:'#ccpn',
								methods:{
									btnClick:function(){
										console.log(this.$parent.name)
										this.$parent.showMessage()
									}
								},
								data(){
									return {
										name:"我是乔木"
									}
								}
						
							}
						}
					}
					
				}
			})
		</script>
	</body>
</html>

[

$root

访问根,和上述标识符用法相同


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值