组件的注册
在 vue
中,我们可以通过 new Vue
来创建一个组件,不过通常它是作为整个应用的顶层根组件存在的,我们还可以通过另外的方式来注册一个更为通用的组件
Vue.component()
Vue.component('组件名称', {组件选项})
- 组件名称遵循自定义组件命名规范:全小写、连字符(虽然驼峰式一般也没问题)
- 组件选项与
new Vue
选项配置基本一致(也有一些细节的不同)
全局组件与局部组件
通过 Vue.component
注册的组件,我们称为全局组件,因为它可以在任意范围内使用,我们还可以定义局部组件
new Vue({
...,
components: {
'组件名称': {组件选项}
}
})
在一个组件内部通过 components
选项注册的组件是局部组件,只能在当前 components
选项所在的组件内部使用
注意:局部注册的组件只能中当前注册的组件中使用,不能在它的子组件中使用
data
在非 new Vue的组件中,data 必须为函数,函数返回值必须是一个对象,作为组件的最终 data
props
组件中内部私有数据存储中组件 data 中,通过外部传入的数据,则通过 props<选项接收
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<kkb-circle :r="n1"></kkb-circle>
<hr />
<kkb-circle :r="n2"></kkb-circle>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
n1: 10,
n2: 100
},
components: {
'kkb-circle': {
props: ['r'],
data() {
return {pi: 3.14}
},
template: `<div>r: {{r}} -> {{pi * r * r}}</div>`
}
}
});
</script>
</body>
</html>
- 如果传入的 props 值为一个表达式,则必须使用 v-bind
- 组件中的 data 和 prop 数据都可以通过组件实例进行直接访问
- data 中的 key 与 props 中的 key 不能冲突
组件通信
注意:不要修改 props 传入的数据
父组件通过 props 传入数据到子组件内部,但是子组件内部不要修改外部传入的 props,vue 提供了一种事件机制通知父级更新,父级中使用子组件的时候监听对应的事件绑定对应的处理函数即可
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<p>父组件:{{quantity}}</p>
<kkb-child :quantity="initQuantity" @increment="appIncrement"></kkb-child>
<kkb-child :quantity="initQuantity" @increment="appIncrement"></kkb-child>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const child = {
props: ['quantity'],
data() {
return {
n: this.quantity
};
},
template: `
<div>
<p>子组件:{{n}}</p>
<button @click="increment">按钮</button>
</div>
`,
methods: {
increment() {
this.n++;
this.$emit('increment', this.n);
}
}
};
new Vue({
el: '#app',
data: {
quantity: 0,
initQuantity: 0
},
components: {
'kkb-child': child
},
methods: {
appIncrement(v) {
this.quantity++;
}
}
});
</script>
</body>
</html>
$emit()
vue
为每个组件对象提供了一个内置方法 $emit
,它等同于自定义事件中的 new Event
,trigger
等
this.$emit('自定义事件名称', 事件数据)
-
事件数据就是中触发事件的同时携带传递的数据 -
event
-
父级在使用该组件的过程中,可以通过
@事件名称
来注册绑定事件函数 -
事件函数的第一个参数就是事件数据
组件双绑的实现
虽然并不推荐在组件内部修改 props
,但是,有的时候确实希望组件内部状态变化的时候改变 props
,我们可以通过子组件触发事件,父级监听事件来达到这个目的,不过过程会比较繁琐,vue
提供了一些操作来简化这个过程
v-model
v-model
是 vue
提供的一个用于实现数据双向绑定的指令,用来简化 props 到 data
,data 到 props
的操作流程
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.kkb-radio {
width: 20px;
height: 20px;
background-color: #cccccc;
}
.kkb-radio.checked {
background-color: #92beee;
}
.plane {
border: 1px solid #000000;
width: 300px;
}
.plane .header {
width: 100%;
height: 30px;
background: #cccccc;
}
.plane .content {
height: 100px;
width: 100%;
display: none;
}
.plane.expanded .content {
display: block;
}
</style>
</head>
<body>
<div id="app">
<!-- <kkb-radio :checked="val"></kkb-radio> -->
<kkb-radio v-model="val"></kkb-radio>
<br /><br /><br />
<kkb-plane :expanded="val"></kkb-plane>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const kkbRadio = {
model: {
prop: 'checked',
event: 'check'
},
props: ['checked'],
data() {
return {
status: this.checked
}
},
template: `
<div class="kkb-radio" :class="{'checked': status}" @click="changeStatus"></div>
`,
methods: {
changeStatus() {
this.status = !this.status;
this.$emit('check', this.status);
}
}
};
const kkbPlane = {
props: ['expanded'],
template: `
<div class="plane" :class="{'expanded': expanded}">
<div class="header"></div>
<div class="content"></div>
</div>
`
};
let vm = new Vue({
el: '#app',
data: {
val: true
},
components: {
'kkb-radio': kkbRadio,
'kkb-plane': kkbPlane
},
methods: {
}
});
</script>
</body>
</html>
model 选项
prop
指定要绑定的属性,默认是 value
event
指定要绑定触发的事件,默认是 input
事件
.sync
通过 v-model
来进行双向绑定,会给状态维护带来一定的问题,因为修改比较隐蔽,同时只能处理一个 prop
的绑定,我们还可以通过另外一种方式来达到这个目的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.kkb-radio {
width: 50px;
height: 50px;
border: 5px solid #000000;
background-color: #ffffff;
}
.kkb-radio.checked {
border-color: green;
}
.kkb-radio.disabled {
background-color: #cccccc;
}
</style>
</head>
<body>
<div id="app">
<p>val1: {{val1}}</p>
<p>val2: {{val2}}</p>
<hr>
<kkb-radio :checked.sync="val1" :disabled.sync="val2"></kkb-radio>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const kkbRadio = {
props: ['checked', 'disabled'],
data() {
return {
status: this.checked,
dis: this.disabled
}
},
template: `
<div class="kkb-radio" :class="{'checked': status, 'disabled': dis}" @click="changeDis" @mouseover="setChecked" @mouseout="removeChecked"></div>
`,
methods: {
setChecked() {
this.status = true;
this.$emit('update:checked', this.status);
},
removeChecked() {
this.status = false;
this.$emit('update:checked', this.status);
},
changeDis() {
this.dis = !this.dis;
this.$emit('update:disabled', this.dis);
}
}
};
let vm = new Vue({
el: '#app',
data: {
val1: false,
val2: false
},
components: {
'kkb-radio': kkbRadio
},
methods: {
}
});
</script>
</body>
</html>
update:[prop]
这里事件名称要使用 update
加上 prop
名称 的格式