Vue.js(三)

组件化

1. 注册组件的基本步骤
  • 创建组件构造器:调用 Vue.extend() 方法
  • 注册组件:调用 Vue.component() 方法
  • 使用组件:在 Vue实例 的作用范围内使用组件

:这种注册方法得到的是全局组件,可以在多个 Vue 实例下面使用

const cpnConstructor = Vue.extend({
    template: `
    <div>
    	<h3>https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js</h3>
      	<h4>https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js</h4>
      	<h5>https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js</h5>
      	<h6>https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js</h6>
    </div>
    `
})
Vue.component('my-cpn', cpnConstructor);
2. 全局组件和局部组件
  • 全局组件:可以在多个 Vue 实例下面使用
Vue.component('my-cpn', cpnConstructor);
  • 局部组件:只能在该 Vue 实例对象下使用
const app = new Vue({
    el: "#app",
    components: {
      'cpn': cpnConstructor
    }
})
3. 父组件和子组件
<div id="app">
    <cpnfather></cpnfather>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
	// 子组件
	const cpnSonCon = Vue.extend({
    	template: `
    		<div>
      			<h5>https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js</h5>
      			<h6>https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js</h6>
    		</div>
    	`
    })
    // 父组件
    const cpnFatherCon = Vue.extend({
      template: `
    <div>
      <h3>https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js</h3>
      <h4>https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js</h4>
      <cpnson></cpnson>
    </div>
    `,
      components: { // 父组件中注册子组件
        'cpnson': cpnSonCon
      }
    })

    const app = new Vue({
    	el: "#app",
      	components: {
        	'cpnfather': cpnFatherCon,
        	'cpnson': cpnSonCon, // 只有在 root 组件中注册了子组件,才能单独使用子组件
      	}
    })
</script>

注:遇到的问题——注册的标签名不能带大写字母

4. 注册组件语法糖

省去调用 Vue.extend() 的一个步骤,直接使用一个对象来代替

  • 全局组件
Vue.component('my-cpn', {
    template: `
    <div>
      <h3>https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js</h3>
      <h4>https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js</h4>
      <h5>https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js</h5>
      <h6>https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js</h6>
    </div>
    `
});
  • 局部组件
const app = new Vue({
    el: "#app",
    components: {
    	'cpn': {
        	template: `
        		<div>
            		<h3>https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js</h3>
            		<h4>https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js</h4>
            		<h5>https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js</h5>
            		<h6>https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js</h6>
          		</div>
          	`
      	},
    }
})
5. 组件模板抽离的方法
  • script标签
  • template标签
<body>
	<div id="app">
    	<cpn></cpn>
    	<cpn2></cpn2>
  	</div>
	<script type="text/x-template" id="cpn">
    	<div>
        	<h3>https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js</h3>
         	<h4>https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js</h4>
    	</div>
  	</script>
  	<template id="cpn2">
    	<div>
      		<h5>https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js</h5>
      		<h6>https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js</h6>
    	</div>
  	</template>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
	Vue.component('cpn', {
    	template: "#cpn"
  	});
	const app = new Vue({
    	el: "#app",
	    components: {
    		'cpn2': {
        		template: '#cpn2'
      		}
    	}
	})
</script>
6. 组件的data属性
  • 组件的 data 属性是一个函数,返回的是一个对象,对象内部保存着数据
  • 目的:每个组件都有属于自己的对象,如果 data 属性是一个对象,那么多个组件就会共享该 data 对象
<body>
	<div id="counter">
    	<count></count>
    	<count></count>
    	<count></count>
    	<count></count>
	</div>
  	<template id="app">
    	<div>
      		<h2>{{num}}</h2>
      		<button @click="add">+</button>
      		<button @click="sub">-</button>
    	</div>
  	</template>
	<script>
    	Vue.component('count', {
      		template: '#app',
      		data() {
        		return {
          			num: 0,
        		}
      		},
      		methods: {
        		add() {
          			this.num++;
        		},
        		sub() {
          			this.num--;
        		}
			}
    	})

    	const app = new Vue({
      		el: "#counter",
    	})
	</script>
</body>
7. 父子组件间的通信

1)父组件通过 props 向子组件传递数据,方式有两种

  • 字符串数组,数组中的字符串就是传递时的名称
  • 对象,对象可以设置传递时的类型(即类型限制),也可以设置默认值等
<!--- 父组件使用字符串数组与子组件进行通信 --->
<body>
 	<div id="app">
    	<cpnfather :fmovies="movies"></cpnfather>
  	</div>
  	<!-- 父组件模板 -->
  	<template id="movies">
    	<ul>
      	<li v-for="item in fmovies">{{item}}</li>
      		<cpnson :cmovies="fmovies"></cpnson>
    	</ul>
  	</template>
  	<!-- 子组件模板 -->
  	<template id="cmovies">
    	<ul>
      		<li v-for="item in cmovies">{{item}}</li>
    	</ul>
  	</template>

  	<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
  	<script>
  		// 子组件
    	const cpnson = {
      		template: '#cmovies',
      		props: ['cmovies'],
      		data() {
        		return {};
      		}
    	}
    	// 父组件
	  	const cpnfather = {
      		template: '#movies',
      		data() {
        		return {};
      		},
      		props: ['fmovies'], // 数组中的字符串就是传递时的名称
      		components: { //注册子组件
        		cpnson,
      		}
    	}
    	const app = new Vue({
      		el: "#app",
      		data: {
        		movies: ['盗梦空间', '大兵瑞恩', '窈窕淑女', '星际穿越', '海上钢琴师', '死亡诗社'],
      		},
      		components: { // 注册父组件
        		cpnfather,
      		}
    	})
  	</script>
</body>
<body>
  	<div id="app">
    	<cpnfather :fmovies="movies"></cpnfather>
  	</div>
	<template id="movies">
    	<ul>
      		<li v-for="item in fmovies">{{item}}</li>
      		<cpnson :cmovies="fmovies"></cpnson>
    	</ul>
  	</template>
  	<template id="cmovies">
    	<ul>
      		<li v-for="item in cmovies">{{item}}</li>
    	</ul>
  	</template>
	<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
  	<script>
    	const cpnson = {
      		template: '#cmovies',
      		props: { // 使用对象进行传递
      	  		cmovies: { // 对象还可以包括类型限制,必传属性等属性
          			type: Array,
          			required: true,
        		} 
      		},
      		data() {
        		return {};
      		}
	    }

    	const cpnfather = {
      		template: '#movies',
      		data() {
        		return {};
      		},
      		props: {
        		fmovies: Array, // 类型限制
      		}, // 数组中的字符串就是传递时的名称
     	 	components: {
        		cpnson,
      		}
    	}

	    const app = new Vue({
    		el: "#app",
      		data: {
        		movies: ['盗梦空间', '大兵瑞恩', '窈窕淑女', '星际穿越', '海上钢琴师', '死亡诗社'],
      		},
      		components: {
        		cpnfather,
      		}
    	})
  	</script>
</body>
  • 使用对象进行传递数据时的多种写法
props: {
	// 基础的数据检查,此项为 null 时,匹配任何类型
	propA: Number, 
	// 匹配多个类型
	propB: [String, number], 
	// 必填的字符串
	propC: {
		type: String,
		required: true
	},
	// 带有默认值的数字
	propD: {
		type: Number,
		default: 100
	},
	// 带有默认值的对象
	propE: {
		type: Object,
		// 对象或数组默认值必须从一个工厂函数获取
		default() {
			return { message: 'hello' };
		}
	},
	// 自定义验证函数
	propF: {
		validator(value) {
			return ['sucess', 'warning', 'danger'].indexOf(value) !== -1;
		}
	}
}

// 自定义类型
function Person (firstName, lastName) {
	this.firstName= firstName;
	this.lastName= lastName;
}
Vue.component('blog-post', {
	props: {
		author: Person
	}
})
  • 父传子(props的驼峰标识):v-bind 是不支持使用驼峰标识的,例如cUser要改成c-usercuser

2)子组件通过 自定义事件 向父组件发送消息

  • 子组件点击按钮,传递按钮元素内的内容

在这里插入图片描述

<body>
	<div id="app">
    	<cpnson @cpnclick="cpnclick"></cpnson>
  	</div>
	<template id="cmovies">
    	<div>
      		<button @click="btnclick(item)" v-for="(item,index) in movies" :key="index">{{item.name}}</button>
    	</div>
  	</template>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
  	const cpnson = {
    	template: '#cmovies',
    	data() {
      		return {
        		movies: [{
          			id: 1,
          			name: '盗梦空间'
        		}, {
          			id: 2,
          			name: '大兵瑞恩'
        		}, {
          			id: 3,
          			name: '窈窕淑女'
        		}, {
          			id: 4,
          			name: '星际穿越'
        		}, {
          			id: 5,
          			name: '海上钢琴师'
        		}, {
          			id: 6,
          			name: '死亡诗社'
        		}],
      		};
    	},
    	methods: {
      		btnclick(item) {
        		this.$emit('cpnclick', item);
      		}
    	}
  	}

  	const app = new Vue({
    	el: "#app",
    	data() {
      		return {};
    	},
    	methods: {
      		cpnclick(item) {
        		console.log("cpnclick", item)
      		}
    	},
    	components: {
      		cpnson,
    	},
  	})
</script>
  • 子组件是按钮,点击子组件的按钮,改变父组件的值

在这里插入图片描述

<body>
  	<div id="app">
    	<div>{{num}}</div>
    	<cpn @item-Click="op"></cpn>
  	</div>
	<template id="operation">
    	<div>
      		<button @click="decrement">-</button>
      		<button @click="increment">+</button>
    	</div>
  	</template>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
  	const cpn = {
    	template: '#operation',
    	data() {
      		return {
        		add: 'add',
        		sub: 'sub'
      		};
    	},
    	methods: {
      		increment () {
        		this.$emit('item-Click', this.add);
      		},
      		decrement () {
        		this.$emit('item-Click', this.sub);
      		},
    	}
  	}
	const app = new Vue({
    	el: '#app',
    	data() {
      		return {
        		num: 0,
      		};
    	},
    	methods: {
      		op(e) {
        		switch(e) {
          			case 'add': this.num++;
            			break;
          			case 'sub': this.num--;
            			break;
          			default:
            			break;
        		}
      		}
    	},
    	components: {
      		cpn
    	}
  	})
</script>
  • 自定义事件的流程:
    1. 在子组件中,通过 $eemit() 来触发事件
    2. 在父组件中,通过 v-on 来监听子组件

3) 父子组件通信小案例

父组件传递数据到子组件,子组件中的输入框中输入数字,修改父组件中的数据

在这里插入图片描述
在这里插入图片描述

  • input 实现
<body>
 	<div id="app">
    	<h3>num1: {{num1}}</h3>
    	<h3>num2: {{num2}}</h3>
    	<cpn :cnum1="num1" :cnum2="num2" @fnum1change="fnum1change" @fnum2change="fnum2change"></cpn>
  	</div>
  	<template id="cpn">
    	<div>
      		<h3>{{cnum1}}</h3>
      		<h3>{{inum1}}</h3>
      		<input type="text" v-model="inum1" @input="num1change">
      		<h3>{{cnum2}}</h3>
      		<h3>{{inum2}}</h3>
      		<input type="text" v-model="inum2" @input="num2change">
    	</div>
  	</template>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
  	const app = new Vue({
    	el: '#app',
    	data: {
      		num1: 1,
      		num2: 0,
    	},
    	methods: {
      		fnum1change(value) {
        		this.num1 = parseFloat(value);
      		},
      		fnum2change(value) {
        		this.num2 = parseFloat(value);
      		}
    	},
    	components: {
      		cpn: {
        		template: '#cpn',
        		props: {
          			cnum1: {
            			type: Number,
            			default: 9,
          			},
          			cnum2: {
            			type: Number,
            			default: 9,
          			}
        		},
        		data() {
          			return {
            			inum1: this.cnum1,
            			inum2: this.cnum2,
          			};
        		},
        		methods: {
          			num1change(event) {
            			this.$emit('fnum1change', event.target.value);
          			},
          			num2change(event) {
            			this.$emit('fnum2change', event.target.value);
          			}
        		}
      		}
    	}
  	})
</script>
  • show实现
<body>
 	<div id="app">
    	<h2>父组件data</h2>
    	<h3>num1: {{num1}}</h3>
    	<h3>num2: {{num2}}</h3>
    	<cpn :cnum1="num1" :cnum2="num2" @fnum1change="fnum1change" @fnum2change="fnum2change"></cpn>
  	</div>
  	<template id="cpn">
    	<div>
      	<h2>子组件props</h2>
      	<h3>cnum1: {{cnum1}}</h3>
      	<h3>cnum2: {{cnum2}}</h3>
      	<h2>子组件data</h2>
      	<h3>inum1: {{inum1}}</h3>
      	<h3>inum2: {{inum2}}</h3>
      	<input type="text" v-model="inum1"><br />
      	<input type="text" v-model="inum2">
    	</div>
  	</template>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
  	const app = new Vue({
    	el: '#app',
    	data: {
      		num1: 1,
      		num2: 0,
    	},
    	methods: {
      		fnum1change(value) {
        		this.num1 = parseFloat(value);
      		},
      		fnum2change(value) {
        		this.num2 = parseFloat(value);
      		}
    	},
    	components: {
      		cpn: {
        		template: '#cpn',
        		props: {
          			cnum1: {
            			type: Number,
            			default: 9,
          			},
          			cnum2: {
            			type: Number,
            			default: 9,
          			}
        		},
        		data() {
          			return {
            			inum1: this.cnum1,
            			inum2: this.cnum2,
          			};
        		},
        		watch: {
          			inum1(newValue) {
            			this.$emit('fnum1change', newValue);
          			},
          			inum2(newValue) {
            			this.$emit('fnum2change', newValue);
          			}
        		}
      		}
    	}
  	})
</script>
  • show

侦听器:监听数据变化,一般只监听一个变量或数组
使用场景:watch(异步场景),computed(数据联动

8. 父子组件的访问方式

有时候需要直接操作子组件的方法,或是属性,此时需要用到 $children$ref

<!-- 父组件 -->
<div id="app">
	<cpn></cpn>
    <cpn></cpn>
    <cpn ref="aaa"></cpn>
    <button @click="btnClick" >按钮</button>
</div>
<!-- 子组件 -->
<template id="cpn">
    <div>我是子组件</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
	
    const app = new Vue({
    	el: "#app",
      	data() {
        	return {
        	  	message:"hello"
        	}
      	},
      	methods: {
        	btnClick(){
          		// 1.children
          		// console.log(this.$children[0].showMessage)
          		// for (let cpn of this.$children) {
          		//		console.log(cpn.showMessage)
          		// }
          		// 2.$ref
          		console.log(this.$refs.aaa.name)
        	}
      	},
      	components: {
        	cpn: {
          		template: "#cpn",
          		data() {
            		return {
              			name:"我是子组件的name"
            		}
          		},
          		methods: {
            		showMessage(){
              			console.log("showMessage");
            		}
          		},
        	}
      	},
    })
</script>
  • $children 方式
console.log(this.$children[0].showMessage)
for (let cpn of this.$children) {
    console.log(cpn.showMessage)
}

使用this.$children直接获取**当前实例的直接子组件,需要注意 $children 并不保证顺序,也不是响应式的。如果你发现自己正在尝试使用 $children 来进行数据绑定,考虑使用一个数组配合 v-for 来生成子组件,并
且使用 Array 作为真正的来源。

  • $refs 方式
<!-- 先定义子组件,直接调用 -->
<cpn ref="aaa"></cpn>
  • $parent 方式
<body>
	<div id="app">
	    <cpn></cpn>
    	<cpn></cpn>
    	<cpn ref="aaa"></cpn>
  	</div>
	<template id="cpn">
	    <div>
	    	子组件消息:{{message}}
      		<button @click="btnClick" >子组件按钮</button>
    	</div>
  	</template>
	<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
	<script>
    	const app = new Vue({
      		el: "#app",
      		data() {
        		return {
          			message:"我是父组件消息"
        		}
      		},
      		components: {
        		cpn: {
      				template: "#cpn",
      				data() {
        				return {
         					message:"我是子组件的name"
        				}
      				},
      			methods: {
        			btnClick(){
          				console.log("子组件按钮被点击")
          				// 1.访问父组件$parent
          				this.message = this.$parent.message
          				// 2.访问根组件$root
          				console.log(this.$root)
          				console.log(this.$root.message)
        			}
      			}
    		}
	    })
  	</script>
</body>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值