vue2.X基础知识四之组件及父子组件通信

8 篇文章 0 订阅
一、组件注册

1、在Vue中全局注册

//my-component就是注册的组件自定义标签名,推荐使用小写加减号分割的形式命名
//template的DOM结构必须被一个元素包含(这里是div),否则无法渲染
Vue.component('my-component', {template: '<div>我是组件</div>'}); 
   var app = new Vue({
            //选择元素
            el: "#app"
        });

在html中使用组件:

<div id="app">
    <my-component></my-component>
</div>

使用全局注册的时候,组件必须先注册,然后再实例化Vue;全局注册的组件,任何Vue实例都可以使用。

2、在Vue实例中,注册局部组件

注册的局部组件只有在该实例作用域下有效。组件也可以使用components选项来注册组件,使组件可以嵌套。

<div id="app">
    <my-component></my-component>
</div>
var child = {
    template: '<div>局部注册组件的内容</div>'
}

var app = new Vue({
    el: '#app',
    components: {
        'my-component': child
    }
});

3、组件中的data

组件中的配置选项与Vue实例很类似,在组件中使用data时,data必须是函数,然后将数据return出去,例如:

<div id="app">
    <my-component></my-component>
</div>
Vue.component('my-component', {
    		template: '<div @click="handleClick($event)">{{ name }}</div>',
    		
    		/* 子组件的data必须是一个函数  */
    		data: function() {
    			return {
    				name: '子组件的数据'
    			}
    		},
    		methods: {
    			handleClick($event){
    				$event.target.style.color = 'red';
    				alert('子组件的click事件');
    			}
    		}
    	});
    	
        var app = new Vue({
            el: "#app"     
        });
二、组件通信

    组件不仅仅是要把模板的内容进行复用,更重要的是组件间要进行通信。通常父组件的模板中包含子组件,父组件要正向地向子组件传递数据或参数,子组件接收到后根据参数的不同来渲染不同的内容或执行操作。props的值可以是两种,一种是字符串数组,一种是对象。

1、父组件向子组件传递数据,使用props来实现

    在组件中,使用选项props来声明需要从父级接收的数据。我们来看一个最简单的例子,直接给子组件传递一个字符串:

<div id="app">
    <my-component message="来自父组件的数据"></my-component>		
</div>
Vue.component('my-component', {
    props: ['message'],
    template: '<div>{{ message }}</div>'
});
    	        
var app = new Vue({
    el: "#app"
});

上面代码中,我们给子组件设置了一个自定义的属性message,它的值是父组件想要传递给子组件my-component的值,这里是固定的一个字符串。运行结果如下:

说明:由于html特性不区分大小写,当使用DOM模板时,驼峰命名的props名称要转为短横分隔命名,比如props:['warningText'],在组件定义这个属性时候应该这样写:warning-text="来自父组件的信息" (如果使用的是字符串模板(单个vue文件),可忽略这些限制)

  props中声明的数据与组件data函数return的数据主要区别就是props的来自父级,而data中的是组件自己的数据,作用域是组件本身,这两种数据都可以在模板template及计算属性computed和方法methods中使用

  有时候,传递的数据并不是直接写死的,而是来自父级的动态数据,这时可以使用指令v-bind来动态绑定props的值,当父组件的数据变化时,也会传递给子组件。


<!--这里用v-model绑定了父级的数据parentMessage,当通过输入框任意输入时,
子组件接收到的props "message"也会实时响应,并更新组件模板。 -->
<div id="app">
    <input type="text" v-model="parentMessage" />
    <my-component :message="parentMessage"></my-component>
</div>
Vue.component('my-component', {
    		props: ['message'],
    		template: `<div>
    			      <p>子组件自身的数据:{{ name }},</p>
    			      <p>来自父组件的数据:<b style="color: red;">{{ message }}</b></p>
    		          </div>`,
    		data: function() {
    			return {
    			    name: '我是子组件自带的数据'
    			}
    		}
    	});
    	
        var app = new Vue({
            el: "#app",
            data: {
            	parentMessage: '我是父组件的message',
            }
});

可以看到,这个例子与上一个例子有个很明显的区别,这里使用v-bind绑定了自定义的属性message的值,message保存了父组件parentMessage的值;子组件通过props接收message保存的值,同时template可以像访问data一样,访问props里面的message;输出效果如下:

注意、如果直接传递数字,布尔值、数组、对象,而且不使用v-bind,传递的仅仅是字符串;例如:

    <div id="app">
        <!-- 第一个组件没有使用v-bind,所以传递给子组件的是字符串"[1,2,3]",所以,输出的length是7 -->
        <my-component message="[1,2,3]"></my-component>
        <!-- 这里是正常输出,length为3 -->
        <my-component :message="[1,2,3]"></my-component>
    </div>
    <script>
    	Vue.component('my-component', {
    		template: `<div>
    			      <p>来自父组件的数据:{{ message.length }}</p>
    		          </div>`,
    		props: ['message'],
    		/* 子组件的data必须是一个函数  */
    		data: function() {
    			return {
    			    name: '我是子组件自带的数据'
    			}
    		}
    	});
        var app = new Vue({
            //选择元素
            el: "#app",
            data: {
            	parentMessage: '我是父组件的message',
            }
        });
    </script>

props单向数据流

Vue2.X通过props传递数据是单向的,也就是父组件数据变化时会传递给子组件,但是反过来不行。通常在业务中经常会遇到两种需要改变prop的情况,代码如下:

<div id="app">
    <my-component :init-count="parentData"></my-component>
		
    <!-- 在子组件中自定义属性最好不要与html元素已有的属性名一样 -->
    <my-component-2 :styles="style"></my-component-2>
</div>
<script>
    	/*
    	 需要改变prop的情况一:
    	    父组件传递初始值过来,子组件将它作为初始值保存起来,在自己的作用域下
    	 可以随意使用和修改。这种情况可以在组件data内再声明一个数据,引用父组件的prop;   
    	 * 
    	 * */
    	Vue.component('my-component', {
    		props: ['initCount'],
    		template: '<div>{{ count }} <input type="text" v-model="count" /></div>',
    		data: function () {
    			return {
    				count: this.initCount
    			}
    		}
    	});
    	
    	/*
    	 另一种情况就是prop作为需要被转变的原始值传入。这种情况用计算属性就可以了;
    	 用css传递宽度要带单位(px),但是每次都写太麻烦,而且数值计算一般是不带单位的,
    	 所以统一在组件内使用计算属性就可以了。
    	 * */
    	Vue.component('my-component-2', {
    		props: ['styles'],
    		template: '<div :style="style">组件内容</div>',
    		computed: {
    			style: function () {
    				return {
    					width: this.styles.width + 'px',
    					color: this.styles.color,
    					border: this.styles.border
    				}
    			}
    		}
    	});
    	
    	
    	var app = new Vue({
    		el: '#app',
    		data: {
    			parentData: 1,
    			style: {
    				color: '#e6b5a7',
    				width: 600,
    				border: '1px solid #e7e8e9'
    			}
    		}
    	}); 	
</script>

以上两种情况,子组件获取父组件的数据之后随意修改,都不会再影响父组件的数据。


props数据验证

    一般当你的组件需要提供给别人使用时,推荐都进行数据验证;如果传入的值类型不对,就会在控制台弹出警告。例如:

<div id="app">
    <my-component :prop-a="parentData" :prop-d="count"></my-component>
</div>
Vue.component('my-component', {
	                    template: '<div>props的值为数组时,接收父组件数据:{{ propA }},接收数值类型:{{ propD }}</div>',
			    props: {
				// 必须是数字类型
				propA: Number,
				// 必须是字符串或数字类型
				propB: [String, Number],
				// 布尔值,如果没有定义,默认值就是true
				propC: {
					type: Boolean,
					default: true
				},
				//数字,而且是必传
				propD: {
					type: Number,
					required: true
				},
				//如果是数组或对象,默认值必须是一个函数来返回
				propE: {
					type: Array,
					default: function () {
						return [];
					}
				},
				//自定义一个验证函数
				propF: {
					validator: function (value) {
						return value > 10;
					}
				}
			}
		});
    	var app = new Vue({
    		el: '#app',
    		data: {
    			parentData: 123,
    			count: 456
    		}
    	});

以上代码如果我们在子组件里不传入prop-d,就会在控制台报错,因为propD是必传项;验证的type类型可以是:String,Number,Boolean,Object,Array,Function;type也可以是一个自定义构造器,使用instanceof检测。

2、子组件向父组件传递数据,使用自定义事件

 子组件需要向父组件传递数据时,就要用到自定义事件;v-on除了监听DOM事件外, 还可以用于组件之间的自定义事件。

 子组件用$emit来触发事件,父组件用$on()来监听子组件的事件。 父组件可以直接在子组件的自定义标签上使用v-on(语法糖@)来监听子组件触发的自定义事件

<div id="app">
    <p>总数:{{ total }}</p>
    <my-component 
		@increase="handleGetTotal"
		@reduce="handleGetTotal"></my-component>
</div>

以上代码使用@(v-on)监听子组件的increase和reduce自定义事件,一旦子组件触发了这两个事件,父组件就会立即执行handleGetTotal方法,同时接收子组件传递过来的参数(数据);

		Vue.component('my-component', {
			template: `
				<div>
					<button @click="handleIncrease">+1</button>
					<button @click="handleReduce">-1</button>
				</div>
			`,
			data: function () {
				return {
					counter: 0
				};
			},
			methods: {
				handleIncrease: function () {
					this.counter++;
					// 触发自定义的increase事件,同时将自身的counter数据传递过去
					this.$emit('increase', this.counter);
				},
				handleReduce: function () {
					this.counter--;					
					// 触发自定义的reduce事件
					this.$emit('reduce', this.counter);
				}
			}
		});
		
		var app = new Vue({
			el: '#app',
			data: {
				total: 0
			},
			methods: {
				handleGetTotal: function (total) {
					this.total = total;
				}
			}
		});
子组件在按钮上绑定click事件,当点击按钮时,执行自身methods里的方法,这两个方法使用vue实例的$emit来触发自定义事件increase或reduce;

除了用v-on在组件上监听自定义事件外,也可以监听DOM事件,这时可以用.native修饰符表示监听的是一个原生事件,监听的是该组件的根元素。例如:

<div id="app">
    <!-- 监听原生事件 -->
    <my-component v-on:click.native="handleClick"></my-component>
</div>
var app = new Vue({
    el: '#app',
    data: {
	total: 0
    },
    methods: {
		handleGetTotal: function (total) {
		    this.total = total;
		},
		handleClick: function () {
		    alert('监听子组件的原生click事件的处理函数要写在父组件上');
		}
	}
});

Vue 2.X可以在自定义组件上使用v-model指令,示例如下:

<div id="app">
		<p>总数:{{ total }}</p>
        <my-component v-model="total"></my-component>
        
        <my-component-2 @input="getParentData"></my-component-2>
    </div>
    <script>
    	Vue.component('my-component', {
    		template: '<button @click="handleClick">+1</button>',
    		data: function () {
    			return {
    				counter: 0
    			}
    		},
    		methods: {
    			handleClick: function () {
    				this.counter++;
    				this.$emit('input', this.counter);
    			}
    		}
    	});
    	
    	Vue.component('my-component-2', {
    		template: '<button @click="handleIncrease">+1</button>',
    		data: function () {
    			return {
    				counter: 0
    			}
    		},
    		methods: {
    			handleIncrease: function () {
    				this.counter++;
    				this.$emit('input', this.counter);
    			}
    		}
    	});
    	
    	//以上两种写法,效果是一样的;只是v-model更简洁,方便,不需要使用自定义属性
    	
        var app = new Vue({
            el: "#app",
            data: {
            	total: 0
            },
            methods: {
            	getParentData: function (total) {
            		this.total = total;
            	}
            }
        });
    </script>

使用v-model指令创建自定义的表单输入组件:

<div id="app">
		<p>总数:{{ total }}</p>
        <my-component v-model="total"></my-component><br/>
        <button @click="handleReduce">-1</button>
</div>
Vue.component('my-component', {
    		props: ['value'],
    		template: '<input :value="value" @input="updateValue" />',
    		methods: {
    			updateValue: function (event) {
    				this.$emit('input', event.target.value);
    			}
    		}
    	});
    	
        var app = new Vue({
            el: "#app",
            data: {
            	total: 0
            },
            methods: {
            	handleReduce: function () {
            		this.total--;
            	}
            }
});

以上代码执行过程个人理解:

通过v-model和props将父组件的total值传递给了input;

 input使用v-bind绑定了父组件传递过来的值;同时绑定了input事件,当输入新的input值时,通过updateValue处理函数

 执行$emit('input', event.target.value)将输入的新值传回给父组件的total;(这里面发生了父组件向子组件传值以及子组件给父组件传值的过程,都得益于v-model)

显示效果如下:


文章说明:本文大部分内容来自书籍 《Vue.js实战》





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值