# 组件详解
>[success] 可在此处查看运行文档中的代码示例结果:[https://github.com/jianyaoo/Vue](https://github.com/jianyaoo/Vue)
## 基本使用
> 我们在注册一个组件的时候都会给组件起一个组件名,起组件名的两种方式为`kebab-case`(全部都是小写字母,中间用-连接)和`PascalCase`(首字母大写)。当时用连字符的时候,可能使用连字符的方式进行调用。如果使用首字母大小写的方式,两种方式都可以调用。
### 全局注册组件
**<span style="padding-top:0px;display:inline-block;">注册方式</span>**
使用`Vue.component`方式注册的组件都为全局注册组件,全局注册组件可以在任何新创建的Vue根实例模板中
**<span style="padding-top:20px;display:inline-block;">代码实例</span>**
~~~html
<h3>全局注册组件</h3>
<div>
<el-glob></el-glob>
</div>
~~~
~~~JavaScript
Vue.component('el-glob',{
template:"<div>hello , component</div>"
})
~~~
<div style="background:#333;color:#fff;padding:15px;">
运行结果:
<div>
<h3>全局注册组件</h3>
<div>hello , component</div>
</div>
</div>
### 实例内注册组件
**<span style="padding-top:0px;display:inline-block;">注册方式</span>**
实例内注册即在Vue的实例内使用`component`属性进行注册的方式。
**<span style="padding-top:20px;display:inline-block;">代码实例</span>**
~~~
<h3>实例内注册</h3>
<div>
<el-local></el-local>
</div>
~~~
~~~
// 声明一个组件的属性
var local = {
template: "<div>这是一个实例内注册的组件</div>"
}
// 在根实例中调用组件
var vm = new Vue({
el:"#app",
components:{
"el-local":local
}
})
~~~
<div style="background:#333;color:#fff;padding:15px;">
运行结果:
<div>
<h3>实例内注册</h3>
<div>这是一个实例内注册的组件</div>
</div>
</div>
>[danger] component组件属性定义在哪个实例中,组件才可以在哪个实例中使用。而且子组件之间不可以直接调用,如果需要在子组件中调用另外一个子组件,需要在调用组件的属性中使用`component`属性
### 特殊元素使用组件
**<span style="padding-top:0px;display:inline-block;">使用说明</span>**
有一些元素内只能使用特定的元素,例如`table`、`ul`、`ol`等,如果直接在这些元素内使用组件,组件会渲染到父级外面从而得不到自己想要的效果。使用`is`属性可以解决在这些特定组件上使用组件。
**<span style="padding-top:20px;display:inline-block;">代码实例</span>**
~~~
<h3>特殊元素使用组件</h3>
<table>
<tbody is="el-glob">
</tbody>
</table>
~~~
~~~
Vue.component('el-glob',{
template:"<th><td>hello , component</td></th>"
})
~~~
<div style="background:#333;color:#fff;padding:15px;">
运行结果:
<div>
<h3>特殊元素使用组件</h3>
<div>hello , component</div>
</div>
</div>
**<span style="padding-top:20px;display:inline-block;">代码说明</span>**
使用`is`后的代码结构如下:
```
<table>
<th>
<td>hello , component</td>
<th>
</table>
```
在不使用`is`时的代码结构为:
```
<th>
<td>hello , component</td>
<th>
<table>
<tbody></tbody>
</table>
```
### 带数据的组件
**<span style="padding-top:0px;display:inline-block;">使用说明</span>**
在组件模板中如果使用变量,需要在组件中单独使用`data`属性。需要说明的是在组件中的`data`属性不是一个对象,而是一个可以返回一个对象的函数。
**<span style="padding-top:20px;display:inline-block;">代码实例</span>**
~~~
<h3>带数据的组件</h3>
<div>
<my-component></my-component>
</div>
~~~
~~~
Vue.component('my-component' , {
data:function() {
return{
message:"这是一个带数据的组件"
}
},
template:"<div>{{message}}</div>"
})
~~~
<div style="background:#333;color:#fff;padding:15px;">
运行结果:
<div>
<h3>带数据的组件</h3>
<div>这是一个带数据的组件</div>
</div>
</div>
### 组件独立数据
一个独立组件的数据,应该声明在组件内部,只供当前组件一个可以调用,而不能定义在组件外面。容易引起当组件重复使用时发生错误的情况,如下代码所示:
~~~
<h3>组件独立数据</h3>
<h5>反例</h5>
<div>
<el-data></el-data>
<el-data></el-data>
<el-data></el-data>
</div>
~~~
~~~
var data = {
counter:0
}
Vue.component("el-data",{
data:function() {
return data ;
},
template:"<button @click='counter++'>{{counter}}</button>"
})
~~~
<div style="background:#333;color:#fff;padding:15px;">
运行结果:
当点击第一个按钮时,第二个按钮的值和第三个按钮的值都会发生改变。
</div>
正确的实现代码应该如下:
~~~
Vue.component("el-data",{
data:function() {
return {
counter:0
};
},
template:"<button @click='counter++'>{{counter}}</button>"
})
~~~
## 使用prop进行传值
通信组件即可以在不同的组件之间进行传值,传值的方式是使用`prop`属性。html中的属性大小写不敏感,如果使用驼峰法命名的属性在使用的时候需要用连接法。
### 字符串数组传值
**<span style="padding-top:0px;display:inline-block;">简单传值</span>**
~~~html
<h5>简单传值</h5>
<div>
<el-props message="我是父级的参数" my-count="参数内容"></el-props>
</div>
~~~
~~~
Vue.component("el-props" , {
props:["message","myCount"],
template:"<div>{{message}}+{{myCount}}</div>"
})
~~~
<div style="background:#333;color:#fff;padding:15px;">
运行结果:
<h5>简单传值</h5>
<div>我是父级的参数+参数内容</div>
</div>
**<span style="padding-top:20px;display:inline-block;">动态传值</span>**
~~~
<h5>动态传值</h5>
<div>
<input type="text" v-model="parentMessage">
<el-props :message="parentMessage"></el-props>
</div>
~~~
~~~
Vue.component("el-props" , {
props:["message","myCount"],
template:"<div>{{message}}</div>"
})
~~~
<div style="background:#333;color:#fff;padding:15px;">
运行结果:
div内的内容随着输入框中的内容发生变化而变化
</div>
### 单向数据流
所有的数据流都使得prop的数据由父级向下的一个单项绑定流动:即父级prop的更新会向下流动到子组件,但是子组件的prop更新不会影响到父级。这个就是prop数据的单向数据流。
>[info] 从父组件传下来的prop,每次父组件发生更新时,子组件的值都会自动刷新到最新值。所以一般不应该在子组件内部修改prop的值。
存在两种需要修改子组件prop的值得情况:
**<span style="padding-top:20px;display:inline-block;">prop作为初始值</span>**
如果子组件接受到prop值后希望将prop值作为一个自己的参数使用,传递过来的prop值只是作为一个初始值。这种情况下应该在子组件内部定义一个变量来接收该参数。
~~~
props:["counter"],
data:function(){
return {
count:this.counter
}
},
~~~
**<span style="padding-top:20px;display:inline-block;">作为一个初始值且需要对prop值进行经常变化</span>**
当需要对prop值进行一定的逻辑处理的时候,就需要定义一个计算属性来接收prop值。
~~~
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
~~~
### prop验证
当子组件接受到一个prop时,可以对prop进行验证,如果验证不通过的话会在控制台打印出警告信息。主要用于开发调试
~~~
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
}
}
}
})
~~~
## 插槽