[TOC]

组件定义

首先规定我们组件名称为my-com,我们可以以如下形式在html中使用它 , 之后我们将介绍组件定义的三种方式, 每种定义所用组件名均为my-com

1
2
3
<div id="app">
<my-com></my-com>
</div>
  1. 在Vue.component中编写模板
1
2
3
Vue.component('my-com',{
template: '<h3> this is my component </h3>'
})
  1. 在Vue实例中定义私有组件
1
2
3
4
5
6
7
8
9
10
11
<script>
let mc = {
template: '<h3> this is my component - </h3>'
}
new Vue({
el: '#app',
components: {
'my-com': mc
}
})
</script>
  1. Vue.extend定义组件,Vue.component注册组件,也可以用在Vue实例中
1
2
3
4
5
6
var mc = Vue.extend({
template: '<h3> this is my component </h3>'
});

Vue.component('my-com',mc);

  1. render渲染组件,一般在webpack中这样写,另外,使用render渲染的组件会覆盖挂载实例
1
2
3
4
5
6
7
8
var login = {
template: ''
}

let app = new Vue({
el: '#app',
render: mountComponent => mountComponent(login) // login为组件名
})

组件模板也可以这样写:在Vue.component中定义组件,在html中编写模板,这样有语法高亮和自动补全

1
2
3
4
5
6
7
8
9
<template id="mc">
<h3> this is my component ! </h3>
</template>

<script>
Vue.component('my-com', {
template: '#mc'
})
</script>

插槽

这里将会讲些插槽的基本使用、插槽默认之、具名化插槽一些知识

如果组件不加插槽slot,那么在html代码中,所有组件内部的代码都会被覆盖,比如下面这段代码,my-com组件下的h1标签和内容都不会被展示,只会被组件本身覆盖:

1
2
3
4
5
6
7
8
9
10
11
12
13
<div id="app">
<my-com c='a'>
<h1> xx </h1>
</my-com>
</div>


---
template: `
<div>
<p> 1 </p>
</div>
`

$ show 1

有了slot,我们就可以在html组件代码内部插入其他html代码(如果不插入,则使用插槽的默认值,这里是hhh):

1
2
3
4
5
6
template: `
<div>
<p> 1 </p>
<slot> hhh </slot>
</div>
`

$ show 1

​ xx

每一个插槽都有一个名字,可以使用name指定,如果没有指定,那么隐含名为default

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Vue.component('my-component', {
template: `
<button>
button
<slot name="header">slot header</slot>
<slot name="body">slot body</slot>
<slot name="footer">slot footer</slot>
</button>
`
});

---------
<div id="app" style="font-size:20px">
<my-component>
<template v-slot:header>
<p> 用户指定插槽的header </p>
</template>
<template v-slot:footer>
<p> 用户指定插槽的footer </p>
</template>
</my-component>
</div>

动态绑定

有些组件只能存在于特定的位置,比如<table>只能存在<tr> , 对于这样的情况,我们可以用is指定组件:

1
2
3
4
5
<table>
<tr is="my-row"></tr>
</table>

Vue.component('my-row',{...})

这样还可以做成动态组件,比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
<keep-alive>
<!-- 修改comoose的值,就可以动态改变页面上这个位置的组件 -->
<component v-bind:is="comoose">

</component>
</keep-alive>

new Vue({
el: '',
data: {
comoose: 'com1'
}
})

一个动态组件标签切换的小demo:


组件中的Options

一个组件的基本结构大致如下:

1
2
3
4
5
6
7
8
9
{
inheritAttrs: false, // 禁止继承根元素的属性,inheritAttrs不会影响style和class的绑定
props: [] | {}, // 接受html代码中传过来的属性值
model:{}, // 覆盖默认绑定的值和方法,(checkbox默认绑定value值和click方法)
data: {}, // 组件私有数据
methods: {}, // 组件私有方法
computed: {}, // 组件的计算属性
template: `` // 组件模板
}

概述

一个示例代码(从<body></body>):

  • 实现了一个计数器,并且鼠标悬停会有’this is a title in vue instance’的提示语
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
<div id="app">
<my-com v-bind:title='tl'></my-com>
</div>

<script>
Vue.component('my-com', {
// 接受父组件传递的参数,父组件的属性名 == 子组件接受的变量名
props: ['title'],
// 这里的data是函数,于是每一个对象都可以拥有自己的独立作用域,在组件复用的时候不会相互影响
data: function () {
return {
count: 1
}
},
// 定义了组件的私有方法,此处为increase,每次点击按钮count的值+1
methods: {
increase: function () {
this.count += 1
}
},
// 模板定义组件样式,此处为button按钮
template: '<button @click="increase" :title="title"> click me {{ count }} </button>'
})

new Vue({
el: '#app',
data: {
tl: 'this is a title in vue instance'
}
})
</script>

props

props可以用数组接受参数,但是这样接受的参数不能进行类型校验,想要进行类型校验,我们可以用对象来接受参数

props类型检查:https://cn.vuejs.org/v2/guide/components-props.html#

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
Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
propA: Number,
propB: [String, Number],// 多个可能的类型
propC: { // 必填的字符串
type: String,
required: true
},
propD: { // 带有默认值的数字
type: Number,
default: 100
},
// 带有默认值的对象
propE: { // 对象或数组默认值必须从一个工厂函数获取
type: Object,
default: function () {
return { message: 'hello' }
}
},
propF: { // 自定义验证函数
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})
1
2
3
4
5
6
7
8
9
props: {
title: String,
likes: Number, // 如果不是Number,则会转为Number
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // or any other constructor
}

例子:

如果传递的值不是Number,则显示原值,控制台输出error:Invalid prop: type check failed for prop "v". Expected Number with value NaN, got String with value "12px"

这里依旧展示为:12px

1
2
3
<div id="app">
<my-com :v="msg"></my-com>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Vue.component('my-com', {
props: {
v: Number,
require: true
},
template: '<p> {{ v }} </p>'
})

new Vue({
el: '#app',
data: {
msg: '12px'
}
})

组件间通信

组件间通信可以通过props实现,也可以通过子组件触发父组件函数的方式来实现,下面介绍一下函数的实现方式:

  1. 首先在子组件和Vue实例中分别定义一个函数
  2. 在html代码和html模板中分别调用这个函数
  3. 在模板方法中使用this.$emit(funcName)触发父组件的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="app">
<my-com @func='showmsg'></my-com>
</div>

<script>
Vue.component('my-com', {
methods: {
show: function(){
this.$emit("func");
}
},
template: '<button @click="show"> click me </button>'
})

new Vue({
el: '#app',
methods: {
showmsg: function(){
alert("i'm in the vue instance");
}
}
})
</script>

Vue组件间通信-函数传递数据

this.$emit(funcName , args ); 这个方法接受的第一个参数为函数名, 之后可以传任意参数, 均作为数据部分传递给funcName, funcName可以这样定义:

1
2
3
4
5
funcName: function(data){
console.log(data)
}
,,,,
this.$emit(funcName,'hello');

组件中的this

下面介绍this中的常用方法和属性

1
2
3
4
5
6
7
8
9
10
11
12
13
$attrs: (...) 		// 从父组件那接收到的所有属性,对象类型,结构为: { key: value}
$children: []
$el: div // 当前挂载的DOM元素,就是template的根元素
$listeners: (...) // 监听器,可以重写监听器实现多元素事件绑定
$options: // 记录了Vue的父节点等信息
$parent: // 同$parent = $options.parent
$refs: {} // 用户获取声明的DOM元素
$root: Vue // 当前组件的根节点,是html代码中组件位置所属的根节点
$slots: {} // 插槽
$vnode: VNode
$data: (...) // 组件私有变量
$props: (...) // 接受的属性参数

$attrs的用法:

this.$attrs

$listeners的用法(多元素下的单独事件绑定):

this.$listeners

$model的用法(重写默认事件和属性)

实现点击checkbox,选中为true,取消为false的效果:

model重写

  • checkbox 和 radio 使用 checked property 和 change 事件;
  1. 点击勾选框,触发click事件
  2. click函数中$emit-switch事件
  3. 向switch函数中传入$event.target.checked,改变checkVal的值
  4. 原来的change方法被重写为switch
  5. 原来的value属性被重写为checkVal

click –> switch($event.target.checked) –> checkVal

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>10-20 Vue</title>
<script src="../lib/vue.js"></script>
</head>

<body>
<div id="app">
<my-com v-model="checkVal"></my-com>
</div>

<!-- 定义模板 -->
<template id="mytmp">
<div>
<input type="checkbox" v-bind:checked='checkVal' v-on:click="$emit('switch',$event.target.checked)">
{{ checkVal }}
</div>
</template>

<script>
// 模板处理
Vue.component('my-com', {
model: {
prop: 'checkVal',
event: 'switch'
},
props: ['checkVal'],
template: '#mytmp'
});
// Vue实例
new Vue({
el: '#app',
data: {
checkVal: false
}
})
</script>
</body>

</html>