# 組件詳解
>[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
}
}
}
})
~~~
## 插槽