# 组件的注册

  • 创建组件构造器
  • 注册组件
  • 使用组件
//使用
<div id='app'>
    <my-cpn></my-cpn>
</div>
//创建
<script>
    //这种注册方式不推荐使用
	const myComponent = Vue.extend({
        template:`
			相似于html中的body
		`
    });
</script>
//注册(起个名字)
Vue.component('my-cpn',myComponent);
let app = new Vue({
	el:'#app'
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

第一步通过vue.extend({template:模板代码}),获得组件的容器,第二步通过vue.component(自定义的组件的标签名,容器);第三步使用组件:<自定义标签名><\自定义标签名>注意:该标签只有在vue的挂载的元素内,浏览器才认识(另外通过该种方式创建的组件是全局组件),在vue实力内部创建的组件是局部组件

# 父子组件

父组件的挂载的元素使用了组件的话,默认的情况下会先从该挂载的vue实例中的components中寻找局部组件,如果局部组件不存在,则从全局组件中进行查找,如果没找到则会报红

# 全局组件和局部组件

全局组件:全局都可以使用
局部组件:只有挂载的元素下才可以进行使用

全局组件的语法糖

为了简化注册流程,提供注册语法糖。主要省掉了Vue.extend()的步骤,而是直接使用一个对象来代替

//语法糖的注册方式(创建和注册放到一块了)
 	Vue.component('my-cpn',{
        template:`
			相似于html中的body	
	`});
//注册局部组件的语法糖与全局的相似
const app = new Vue({
	el:'#app',
	data:{
		message:'星辰大海'
	},
	components:{
		'cpn2':{
        template:`
			相似于html中的body	
	`}
	}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 组件模块的分离写法

	//第一种写法(其中的id属性其实是与自定义的组件名进行绑定的)

	//第三步创建组件的模板
	<script type="text/x-template" id="cpn">
		<div>
        	<h2>组件标题</h2>
        	<p>组件的内容</p>
        </div>
	</script>
	第一步引入vue文件
	<script src='../js/vue.js'></script>
	第二步创建vue实例,然后再创建自己的组件
	<script>
		let app = new vue({
            el: '#app',
            data:{
                message: '前面的征途'
            },
            components:{
                'my-cpn':{
                    template:'#cpn'
                }
            }
        })
	</script>
	//第二种写法
	<template id="myCpn">
        <div>
            <h2>我是组件的第二种方式</h2>
			<p></p>
        </div>
	</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# 组件数据的存放

# 为什么不放vue实例的data中?

主要原因是:如果所有的数据都存储到这里,会导致区分特别麻烦

# 组件数据的存放的位置

组件自己也有data属性,但是只能存放函数,不能存放对象 但是可以通过这个函数返回一个对象,对象内部保存着数据

vue.component('cpn',{
	template: '#cpn',
	data() {
		return {
			title :'abc'
		}
	}
})
1
2
3
4
5
6
7
8

# 组件中为什么data只能是函数?

首先,组件的产生是为了能更好的复用。如果组件的data是一个对象,当所有引用该组件的地方,其实获得是相同的数据对象,所以一改则会改全部。只用通过函数的类型返回个不同的数据对象的时候,才能达到独立处理的效果

# 父子组件的通信

# 通信的原因

按vue自定义的规则来讲,子组件是不能引用父组件或者vue实例的数据。但是正常的情况下,我们往往需要从上层向下层传递数据进行逐级的展示。

# 通信的方式

父->子 通过props(properties)向子组件传递消息

const cpn ={
	template: '#cpn',
	//1.数组的方式(缺点:看着有点像字符串,并不知道其属性是什么)
	props: ['cmovies','cmessage'],
	//2.通过对象的方式的进行属性、类型、默认值等的限制(推荐使用)
	props: {
		cmessage:{
			type: String,
			default: 'adad',
			required: true
		}
		//数组和对象的默认值有点特殊,默认值必须是一个函数
		cmoives:{
			type: Array,
			default() {
				return []
			}
		}
	},
	//组件中data必须是函数的属性
	data(){
		return {}
	},
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

驼峰命名存在的问题

<div id='app'>
	v-bind是不支持驼峰的,否则会产生找不到该变量
	通过v-bind使其与父组件的data数据进行绑定,从而使得可以进行访问
	<cpn :c-info="info" :child-my-message="message"></cpn>
</div>
<template>
	//模板中当包含多个元素时,必须含有根标签
</template>
//引入vue.js文件
<script src="../js/vue.js"></script>
<script>
	const cpn ={
		template:'#cpn',
		props:{
			cInfo:{
				type:Object,
				default(){
					return
				}
			}
		}
	}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

子->父 通过事件($emit Events)向父组件发送消息

<div id='app'>
	v-bind是不支持驼峰的,否则会产生找不到该变量
	通过v-bind使其与父组件的data数据进行绑定,从而使得可以进行访问
	<cpn :c-info="info" :child-my-message="message"></cpn>
</div>
<template>
	//模板中当包含多个元素时,必须含有根标签
</template>
//引入vue.js文件
<script src="../js/vue.js"></script>
<script>
	const cpn ={
		template:'#cpn',
        methods:{
            btnClick(item){
                //向父组件发送事件,另外发送的事件的命名规则暂时不要使用驼峰式(学完Spring Boot可以使用)
                this.$emit('item-click',item);//相当于是一个自定义的事件
                //发送到父组件之后,父组件通过v-on进行监听,如果发送则响应相应的函数进行处理
            }
        }
	}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# v-model本质


<input type="text" v-model="dnumber2">
//等同于下面这句话
<input type="text" :value="dnumber2" @input="num2Input">
const app = new Vue({
	el:'#app',
	data:{
		num1:1,
		num2:2
	},
	methods:{
		num2change(value){
			return this.dnumber1;
		}
	},
	components:{
		cpn:{
			template:'#cpn',
			props:{
				number1:Number,
				number2:Number
			},
			data(){
				return{
					dnumber1:this.number1,
					dnumber2:this.number2
				}
			},
			methods:{
				num2Input(event){
					this.dnumber1 = event.target.value;
					this.$emit('num2change',this.dnumber1);
				}
			}
		}
	}
})

//第二种实现方式()通过watch事件进行监听
const app = new Vue({
	el:'#app',
	data:{
		num1:1,
		num2:2
	},
	methods:{
		num2change(value){
			return this.dnumber1;
		}
	},
	components:{
		cpn:{
			template:'#cpn',
			props:{
				number1:Number,
				number2:Number
			},
			data(){
				return{
					dnumber1:this.number1,
					dnumber2:this.number2
				}
			},
			methods:{
				num2Input(event){
					this.dnumber1 = event.target.value;
					this.$emit('num2change',this.dnumber1);
				}
			},
			watch:{
				dnumber1(newValue){
					this.dnumber2 = newValue * 100;
					this.$emit('num1change',newValue);
				},
				dnumber2(newValue){
					this.dnumber1 = newValue / 100;
					this.$emit('num2change',newValue);
				}
			}
		}
	}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

# 父子组件的访问方式

# 父组件访问子组件

使用$children 或$refs(reference)(翻译为:引用) 使用$children(不推荐),使用该语法获取其下的所有组件,但是他是对以数组的方式进行返回的。所以当我们对其下的组件进行访问的时候,我们只能通过下标的方式进行。但是一旦子组件增加的时候,所以对应的下标便会发生改变

btnClickc(){
	console.log(this.$children);
	for(let c of this.$children){
		console.log(c.name);
		c.showMessage();
	}
}
1
2
3
4
5
6
7

使用$refs=> 对象类型,默认是一个空的对象 必须给其子组件写属性ref=' ',产生唯一的标识(通过给这个标识,从而达到避免上面情况的发生)!!!经常使用

console.log(this.$refs.aaa.name);
1

# 子组件访问父组件

使用$parent 或者$root 访问父组件$parent(因为耦合性太强不推荐使用),若是访问了父组件,但是访问了父组件的某些属性,如果要是把他放到其他的组件中,但是其父组件不一定有其相关的属性 访问根组件$root(this.$root.message)(用的相对比较少)

# 组件化高级

# slot插槽的基本使用

插槽的定义:将共性抽取到组件,将不同暴露为插槽。一旦我们预留了插槽,就可以让使用者更具自己得需求,决定插槽中插入什么内容

<slot></slot>
<div id="app">
    <cpn></cpn>//不写的话使用默认值
    <cpn><p>自定义的卡槽接口</p></cpn>//如果里面有多个元素标签,全部作为替换元素
</div>
<template id="cpn">
	<div>
        <h1>未来</h1>
        <p>征途将是星辰大海</p>
        <slot><button>默认值</button></slot>
    </div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12

具名插槽:

<div id="app">
    <cpn></cpn>//不写的话使用默认值
    <cpn><p solt="left">自定义的卡槽接口</p></cpn>//替换指定插槽的值
</div>
<template id="cpn">
	<div>
        <h1>未来</h1>
        <p>征途将是星辰大海</p>
        <slot name="left"><button>左边</button></slot>
        <slot name="center"><button>中间</button></slot>
        <slot name="right"><button>右边</button></slot>
    </div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13

# 编译的作用域

//vue实例的作用域(只会在vue实例的data里面拿数据)
<div id="app">
    <cpn v-show="isShow"></cpn>
    <cpn v-for="item in names"></cpn>
    //属性里面的变量isShow和names都是将从data中进行拿
</div>

//局部作用域,如果是组件内部模板使用了某些变量将优先从组件的内部的函数data中获取属性
<template id="cpn">
	<div>
       <h1>未来的征途将是星辰大海</h1> 
       <button v-show="isShow">按钮</button>
        //这里面的isShow属性将从cpn组件中的data()进行拿去
    </div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 作用域插槽

定义:父组件替换插槽的标签,内容由子组件进行提供。(父组件对子组件的展示效果不满意,需要自定义展示效果,但是数据的来源依然是子组件)

第二步  先使用,后修改自己想要的样式
<cpn>
<div>
    //为了更好的兼容所有的版本,vue2.多之后  直接修改即可
    //绑定插槽对象,获取子组件中的数据
    <template slot-scope="slot">
		<span v-for="item in slot.data">{{item}} - </span>
		//join(指定数据以某种方式进行链接)
		<span>{{slot.data.join(" * ")}}</span>
    </template>
</div>
</cpn>

第一步 将展示的内容放入插槽,只用这样我们才能有自定义的权限
<template id="cpn">
	<div>
       <h1>未来的征途将是星辰大海</h1> 
       <button v-show="isShow">按钮</button>
       	<slot>
       		//绑定组件中的数据
       		<ul :data="dataSource">
       			<li v-for="item in dataSource">{{item}}</li>
       		<ul>
		</slot>
    </div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 组件常见问题

  1. 如果要给插槽绑定元素的时候需要给插槽另外嵌套一个元素标签,在元素标签里面对其操作。因为插槽里面执行相关操作,不一定会被实现
  2. 组件数据的来源,默认的情况下,只与作用域有关——即所属于的模板用自己的数据,如果里面嵌套了其他的组件默认也是使用包装组件的数据
  3. 作用域插槽更多是对子组件的展示效果不太满意,从而通过作用域插槽的方式对子组件数据的展示效果进行修改。