1. 什么是组件化
将一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,方便页面的管理和维护,且扩展性也更强。
2. 组件化的思想
- 我们将一个完整的页面分成很多个组件。
- 每个组件都用于实现页面的一个功能块。
- 每一个组件又可以进行细分。
Vue提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用,而任何的应用都会被抽象成一颗组件树。
3. 注册组件的基本步骤
-
调用Vue.extend创建组件构造器;
在创建组件构造器时,传入template代表我们自定义组件的模板,即要显示的HTML代码。
-
调用Vue.component注册组件;
将组件构造器注册为一个组件,并为它起一个组件的标签名称。需要传递两个参数:1、注册组件的标签名;2、组件构造器。
-
在vue实例的作用范围内使用组件。
组件必须挂载在某个Vue实例下,否则不会生效。beatson1生效,beatson2未生效
4.全局组件和局部组件
全局组件:通过调用Vue.component注册的组件为全局组件。全局组件可以在任意vue实例中使用。
局部组件:在某个vue实例中注册的组件为局部组件。局部组件只能在注册组件的那个vue实例中使用。
component:{
'组件名称': 创建的组件构造器的名称
}
测试
全局组件的效果
局部组件的效果
在这里插入图片描述
测试代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>1组件化的基础实现+全局、局部组件</title>
</head>
<body>
<div id="info1">
<!-- 调用组件-->
<beatson id="beatson1"></beatson>
</div>
<!--<!– 需要挂在到某个vue实例下,才生效–>-->
<!-- <beatson id="beatson2"></beatson>-->
<div id="info2">
<beatson id="beatson3"></beatson>
</div>
<script src="../../js/vue.js"></script>
<script>
// 组件的基本实现
// 创建组件构造器
const beatson = Vue.extend({
template: `
<div>
<p>My name is Daddy!</p>
<p>His name is Son!</p>
<p>I want to beat Son!!</p>
<p>附图一张,不接受反驳!!</p>
<img src="img/wh%20is%20sb.png" alt="wu is sb">
</div>
`
});
// // 注册组件,定义组件标签名称,通过Vue注册是组件为全局组件
// Vue.component('beatson',beatson);
const info1 = new Vue({
el : "#info1",
data : {
},
// 局部组件
components: {
'beatson': beatson,
},
})
const info2 = new Vue({
el : "#info2",
data : {
},
components: {
},
})
</script>
</body>
</html>
5. 父子组件
创建孩子组件构造器
创建父亲组件构造器,并在父组件内部注册子组件
在vue实例内部注册父组件
在vue实例中调用父子组件
效果:子组件实在父组件内被注册的,也只能在父组件内被调用识别。
在vue实例中调用子组件失败,无法识别。
6. 组件语法糖+模块分离
以父子组件为例
组件语法糖
效果
模块分离
方法一:利用script实现
<script type="text/x-template" id="xxx">
html代码……
</script>
方法二:利用template标签实现
<template id="xxx">
</template>
注意:template后面要注册的组件id名,需要用``包起来!!!不是单引号!!是键盘上tab键上面那个键~ `!!!!!
效果
测试代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>3组件语法糖+模板分离</title>
</head>
<body>
<div id="info">
<father_cpn></father_cpn>
<child_cpn></child_cpn>
</div>
<!--<script src="../../js/vue.js"></script>-->
<!-- // 组件语法糖:-->
<!--<script>-->
<!-- const info = new Vue({-->
<!-- el : "#info",-->
<!-- data : {-->
<!-- message : "vue yyds"-->
<!-- },-->
<!-- // 在vue实例内部创建并注册父亲组件构造器-->
<!-- components: {-->
<!-- 'father_cpn': {-->
<!-- template:`-->
<!-- <div id="father">-->
<!-- <p>我是父组件</p>-->
<!-- <p>我还包含了一个子组件</p>-->
<!-- <child_cpn></child_cpn>-->
<!-- </div>-->
<!-- `,-->
<!-- // 在父组件内部创建并注册孩子组件构造器-->
<!-- components: {-->
<!-- 'child_cpn': {-->
<!-- template:`-->
<!-- <div id="child">-->
<!-- <p>我是子组件</p>-->
<!-- <p>我在父组件里面</p>-->
<!-- </div>-->
<!-- `-->
<!-- },-->
<!-- }-->
<!-- }-->
<!-- }-->
<!-- })-->
<!--</script>-->
<!-- 模块分离-->
<!--<!–方法1:利用script实现–>-->
<!--<script type="text/x-template" id="fatherCpn">-->
<!-- <div id="father">-->
<!-- <p>我是父组件</p>-->
<!-- <p>我还包含了一个子组件</p>-->
<!-- <child_cpn></child_cpn>-->
<!-- </div>-->
<!--</script>-->
<!--<script type="text/x-template" id="childCpn">-->
<!-- <div id="child">-->
<!-- <p>我是子组件</p>-->
<!-- <p>我在父组件里面</p>-->
<!-- </div>-->
<!--</script>-->
<!--方法2:利用template标签实现-->
<template id="childCpn">
<div id="child">
<p>我是子组件</p>
<p>我在父组件里面</p>
</div>
</template>
<template id="fatherCpn">
<div id="father">
<p>我是父组件</p>
<p>我还包含了一个子组件</p>
<child_cpn></child_cpn>
</div>
</template>
<script src="../../js/vue.js"></script>
<script>
let info = new Vue({
el : "#info",
data : {
message : "vue yyds"
},
components: {
'father_cpn': {
template: `#fatherCpn`,
components: {
'child_cpn': {
template: `#childCpn`
}
}
}
}
})
</script>
</body>
</html>
7. 组件中的数据存储+组件的data属性必须是一个函数
组件实例都有data属性,该属性是一个函数,返回类型为对象,该对象中存储着组件的数据。
data属性必须是一个函数(返回一个对象,对象中存储组件所需的数据):因为Vue通过data函数使每个组件对象都返回一个新的对象。若共用同一个对象,则多次调用组件时会发生连锁反应互相影响。
以计数器为例
定义计数器组件
构建并注册计数器组件
通过data函数使每个组件对象都返回同一个对象
通过data函数使每个组件对象都返回一个新的对象
效果:
测试代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>4组件的数据存储</title>
</head>
<body>
<div id="info">
<counter id="counter1"></counter>
<counter id="counter2"></counter>
</div>
<template id="counter">
<div>
<p>{
{
num}}</p>
<button @click="decrement">-</button>
<button @click="increment">+</button>
</div>
</template>
<script src="../../js/vue.js"></script>
<script>
// let counterObj = {
// num: 0,
// }
// 计数器组件
Vue.component('counter',{
template: `#counter`,
// date属性必须为函数,返回一个对象,对象中存储数据
// 1.通过data函数使每个组件对象都返回一个新的对象
// data(){
// return counterObj;
// },
// 2.通过data函数使每个组件对象都返回一个新的对象
data(){
return {
num: 0,
}
},
methods: {
increment(){
this.num++;
},
decrement(){
this.num--;
}
}
})
const info = new Vue({
el : "#info",
data : {
message : "vue yyds"
},
methods : {
}
})
</script>
</body>
</html>
8. 组件通信
特别注意:v-bind不支持驼峰标识!!若使用驼峰标识,则需要转化,如:
userName-> v-bind:user-name
记一次报错
原因是:不能含大写字母!!
改成小写字母后运行无bug
1. 父传子:props属性
- 属性是数组: [变量名1,变量名2]
props: ['username','password'],
- 属性是对象:{变量名1:类型1,变量名2:类型2}
props: {
username: String,
password: String
},
- 属性中包含数据验证:
类型:type(String、Number、Boolean、Object、Function、Array、Date、Symbol)
必填:required
默认值:default
自定义验证函数:validator
props:{
username: {
// 类型
type: String,
// 必填
required: true,
// 默认值
default: "admin",
// 对象或数组默认值
// default: function (){
// return {xxx: "xxx"};
// }
},
password: {
type: String,
default: "123456",
// 自定义验证函数
validator: function (value){
return ['123456','root','admin'].indexOf(value) !== -1;
}
}
},
测试代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>5组件通信-父传子</title>
</head>
<body>
<div id="info">
<logininfo :username="user_name" :password="user_password"></logininfo>
<div>
<table>
<thead>
<tr>
<th>用户名</th>
<th>密码</th>
</tr>
</thead>
<tbody>
<tr>
<td>{
{user_name}}</td>
<td>{
{user_password}}</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- 登录组件-->
<template id="logininfo">
<div>
<h3>用户登录</h3>
<form action="">
<label>
用户名:<input type="text" name="username" id="username" placeholder="username" :value="username">
</label>
<label for="password">
密码:<input type="text" name="password" id="password" placeholder="password" :value="password">
</label>
<label for="rememberMe">
<input type="radio" id="rememberMe"> 记住我
</label>
<button> 登 录 </button>
</form>
</div>
</template>
<script src="../../js/vue.js"></script>
<script>
<!-- 子组件-->
const logininfo = {
template: `#logininfo`,
// 属性是数组: [变量名1,变量名2]
// props: ['username','password'],
// 属性是对象:{变量名1:类型1,变量名2:类型2}
// props: {
// username: String,
// password: String
// },
// 属性中包含数据验证
props:{
username: {
// 类型
type: String,
// 必填
required: true,
// 默认值
default: "admin",
// 对象或数组默认值
// default: function (){
// return {xxx: "xxx"};
// }
},
password: {
type: String,
default: "123456",
// 自定义验证函数
validator: function (value){
return ['123456','root','admin'].indexOf(value) !== -1;
}
}
},
}
<!-- vue实例看作父组件-->
let info = new Vue({
el : "#info",
data : {
user_name:"wh",
user_password:"xxxx",
},
components: {
logininfo,
},
})
</script>
</body>
</html>
2. 子传父:事件
子组件通过 $emit() 将自定义事件传递给父组件
父组件通过 v-on 监听子组件传递的事件
子组件模板
定义子组件
通过emit向父组件传递事件时,一定要加上 this.$emit()
父组件模板
此处当子组件中id为red的对象向父组件发起事件请求,则父组件会默认接收来自子组件的red对象
定义父组件
效果:
测试代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>组件通信-子传父</title>
</head>
<body>
<!-- 父组件模板-->
<div id="info">
<child @childbtn="btnClick"></child>
</div>
<!--子组件模板-->
<template id="child">
<div>
<button v-for="item in color"
@click="childClick(item)">
{
{item.name}}</button>
</div>
</template>
<script src="../../js/vue.js"></script>
<script>
// 子组件
const child = {
template: `#child`,
data(){
return {
color: [
{
id: 'red', name: "红色"},
{
id: 'blue', name: "蓝色"},
{
id: 'green', name: "绿色"},
{
id: 'yellow', name: "黄色"},
]
}
},
methods: {
childClick(item){
this.$emit('childbtn', item);
}
}
}
// 把vue实例看作父组件
const info = new Vue({
el : "#info",
components: {
child,
},
methods : {
btnClick(item){
console.log('btnClick', item);
}
}
})
</script>
</body>
</html>