組件詳解
===
[TOC]
# 計數器組件的例子
下面的示例演示 uni-app自定義組件button-counter,每次點擊按鈕計數器增加1。
components/button-counter.vue文件:
```
<template>
<view>
<button type="primary" @click="count++">Clicked {{ count }} times.</button>
</view>
</template>
<script>
export default {
data() {
return {
count: 0,
}
}
}
</script>
<style>
button {
height: 100px;
}
</style>
```
當我們定義這個`<button-counter>`組件時,你可能會發現它的data并不是像這樣直接提供一個對象:
```
data: {
count: 0
}
```
取而代之的是,一個組件的data選項必須是一個函數,因此每個實例可以維護一份被返回對象的獨立的拷貝:
```
data: function () {
return {
count: 0
}
}
```
放置3個button-counter組件,執行的時候發現每一個組件的狀態是獨立的。
```
<template>
<view class="uni-flex uni-column">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</view>
</template>
```

組件是可復用的 Vue 實例,且帶有一個名字:在這個例子中是 `<button-counter>`。我們可以把這個組件作為自定義元素來使用。uni-app目前只支持單文件組件,通過import語句導入組件文件
```
import buttonCounter from '../../components/button-counter.vue'
```
并且在components部分聲明組件,注意命名規則為駝峰寫法。
```
components: {
buttonCounter,
},
```
完整的代碼:
```
<template>
<view class="uni-flex uni-column">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</view>
</template>
<script>
import buttonCounter from '../../components/button-counter.vue'
export default {
components: {
buttonCounter,
},
}
</script>
```
# 使用props傳遞數據
組件實例的作用域是孤立的。這意味著不能并且不應該在子組件的模板內直接引用父組件的數據。可以使用props把數據傳給子組件。prop是組件數據的一個字段,期望從父組件傳下來。子組件需要顯式地用props選項聲明props,以下示例構造一個數字輸入的組件,完整代碼見后面的案例分析。
```
props: {
value: {
type: Number,
default: 0
},
},
```
然后向它傳入一個普通數值:
```
<number-box v-model="number"></number-box>
```
# Vue2中props通信方式
在Vue2中組件的props的數據流動改為了只能單向流動,即只能由組件外(調用組件方)通過組件的DOM屬性attribute傳遞props給組件內,組件內只能被動接收組件外傳遞過來的數據,并且在組件內,不能修改由外層傳來的props數據。

官方文檔給出的解釋:
> prop 是單向綁定的:當父組件的屬性變化時,將傳導給子組件,但是不會反過來。這是為了防止子組件無意修改了父組件的狀態——這會讓應用的數據流難以理解。
也就是說:廢棄了props的雙向綁定,雖然對于整個項目整體而言是有利且正確的,但是在某些時候我們確實需要從組件內部修改props的需求。
案例:
定義一個開關組件:
components/switch-button.vue
```
//開關組件代碼
<template>
<view @click='change'>{{result?'開':'關'}}</view>
</template>
<script>
export default {
props: ["result"],
methods: {
change() {
this.result = !this.result;
}
},
}
</script>
```
引用 :
組件內不能修改props的值,同時修改的值也不會同步到組件外層,即調用組件方不知道組件內部當前的狀態是什么。
那么如何雙向綁定狀態呢?
Vue父子組件傳值通過props和$emit,父組件傳來一個result,這個只在子組件中用props注冊,雖然在子組件中不能直接修改,但是自組建可以通過$emit通知父組件修改,在子組件中定義一個sonReault:this.result,我們只需要通過觀察者模式監聽result 和 sonReault:
```
//開關組件代碼
<template>
<view @click='change'>{{result?'開':'關'}}</view>
</template>
<script>
export default {
props: {
result: {
type: Boolean,
default: false
},
},
methods: {
change() {
this.result = !this.result;
}
},
data() {
return {
myResult: this.result,
}
},
watch: {
result(val) {
this.myResult = val
},
myResult (val) {
this.$emit('toFather', val)
}
}
}
</script>
```

通過這種方式便可以實現父子組件props的雙向綁定。
# 實戰:數字輸入框組件
因為組件是可復用的 Vue 實例,所以它們可以使用 data、computed、watch、methods 以及生命周期鉤子等。
## 數據的雙向綁定
父組件使用props傳遞參數給子組件,但是這是單向傳遞,不能實現雙向數據綁定,通過watch可以實現了數據的雙向綁定。
首先給屬性value聲明一個data副本currentValue,默認引用value的值,然后在組件內部維護這個data。
```
props: {
value: {
type: Number,
default: 0
},
},
data() {
return {
currentValue: this.value,
}
},
```
## 組件定義
完整的示例代碼如下:
components/number-box.vue
```
<template>
<view class="uni-numbox">
<view class="uni-numbox-minus" @click="handleReduce">-</view>
<input class="uni-numbox-value" type="number" :value="currentValue" @change="handleChange" disabled>
<view class="uni-numbox-plus" @click="handleIncrease">+</view>
</view>
</template>
<script>
export default {
props: {
value: {
type: Number,
default: 0
},
},
data() {
return {
currentValue: this.value,
}
},
watch: {
currentValue(val) {
this.$emit('input', val);
this.$emit('on-change', val);
},
value(val) {
this.updateValue(val);
}
},
methods: {
updateValue(val) {
this.currentValue = val;
},
handleChange(event) {
//允許進一步判斷輸入的是否是數字
// event.target.value = this.currentValue;
},
handleIncrease() {
this.$emit('increase');
this.currentValue++;
},
handleReduce() {
this.$emit('reduce');
//最少得留一件
if (this.currentValue <= 1) return;
this.currentValue--;
}
},
mounted() {
this.updateValue(this.value);
}
}
</script>
<style>
.uni-numbox {
display: flex;
flex-direction: row;
height: 50px;
}
.uni-numbox-minus,
.uni-numbox-plus {
margin: 0;
background-color: #f9f9f9;
width: 50px;
line-height: 50px;
justify-content: center;
color: #555555;
font-size: 24px;
border: 0px;
}
.uni-numbox-value {
border: 0px;
width: 100px;
font-size: 24px;
text-align: center;
}
</style>
```
## 代碼分析
首先,在需要引用組件的地方使用@import語句導入組件定義文件
```
import numberBox from '../../components/number-box.vue'
```
并且在component部分申明應用的組件
```
components: {
numberBox
}
```
然后就可以使用組件了。
```
<number-box v-model="number" @increase="handleCount(1)" @reduce="handleCount(-1)"/>
```
> HTML 中的特性名是大小寫不敏感的,所以瀏覽器會把所有大寫字符解釋為小寫字符。這意味著當你使用 DOM 中的模板時,camelCase (駝峰命名法) 的 prop 名需要使用其等價的 kebab-case (短橫線分隔命名) 命名。因此定義組件的時候使用number-box,引用組件的時候使用numberBox。
測試代碼:
```
<template>
<view class="container">
<view class="uni-flex uni-flex-item uni-row">
<number-box v-model="number" @increase="handleCount(1)" @reduce="handleCount(-1)"></number-box>
</view>
</view>
</template>
<script>
import numberBox from '../../components/number-box.vue'
export default {
data: {
number: 1
},
methods: {
handleCount(count) {
console.log('current number:' + this.number);
}
},
components: {
numberBox
}
}
</script>
<style>
.container {
padding: 22px 30px 22px 30px;
}
</style>
```
# 全局組件
uni-app 支持配置全局組件,需在 main.js 里進行全局注冊,注冊后就可在所有頁面里使用該組件。
示例
main.js 里進行全局注冊
```
import Vue from 'vue'
import pageHead from './components/page-head.vue'
Vue.component('page-head',pageHead)
```
index.vue 里可直接使用組件
```
<template>
<view>
<page-head></page-head>
</view>
</template>
```
- 內容介紹
- EcmaScript基礎
- 快速入門
- 常量與變量
- 字符串
- 函數的基本概念
- 條件判斷
- 數組
- 循環
- while循環
- for循環
- 函數基礎
- 對象
- 對象的方法
- 函數
- 變量作用域
- 箭頭函數
- 閉包
- 高階函數
- map/reduce
- filter
- sort
- Promise
- 基本對象
- Arguments 對象
- 剩余參數
- Map和Set
- Json基礎
- RegExp
- Date
- async
- callback
- promise基礎
- promise-api
- promise鏈
- async-await
- 項目實踐
- 標簽系統
- 遠程API請求
- 面向對象編程
- 創建對象
- 原型繼承
- 項目實踐
- Classes
- 構造函數
- extends
- static
- 項目實踐
- 模塊
- import
- export
- 項目實踐
- 第三方擴展庫
- immutable
- Vue快速入門
- 理解MVVM
- Vue中的MVVM模型
- Webpack+Vue快速入門
- 模板語法
- 計算屬性和偵聽器
- Class 與 Style 綁定
- 條件渲染
- 列表渲染
- 事件處理
- 表單輸入綁定
- 組件基礎
- 組件注冊
- Prop
- 自定義事件
- 插槽
- 混入
- 過濾器
- 項目實踐
- 標簽編輯
- 移動客戶端開發
- uni-app基礎
- 快速入門程序
- 單頁程序
- 底部Tab導航
- Vue語法基礎
- 模版語法
- 計算屬性與偵聽器
- Class與Style綁定
- 樣式與布局
- Box模型
- Flex布局
- 內置指令
- 基本指令
- v-model與表單
- 條件渲染指令
- 列表渲染指令v-for
- 事件與自定義屬性
- 生命周期
- 項目實踐
- 學生實驗
- 貝店商品列表
- 加載更多數據
- 詳情頁面
- 自定義組件
- 內置組件
- 表單組件
- 技術專題
- 狀態管理vuex
- Flyio
- Mockjs
- SCSS
- 條件編譯
- 常用功能實現
- 上拉加載更多數據
- 數據加載綜合案例
- Teaset UI組件庫
- Teaset設計
- Teaset使用基礎
- ts-tag
- ts-badge
- ts-button
- ta-banner
- ts-list
- ts-icon
- ts-load-more
- ts-segmented-control
- 代碼模版
- 項目實踐
- 標簽組件
- 失物招領客戶端原型
- 發布頁面
- 檢索頁面
- 詳情頁面
- 服務端開發技術
- 服務端開發環境配置
- Koajs快速入門
- 快速入門
- 常用Koa中間件介紹
- 文件上傳
- RestfulApi
- 一個復雜的RESTful例子
- 使用Mockjs生成模擬數據
- Thinkjs快速入門
- MVC模式
- Thinkjs介紹
- 快速入門
- RESTful服務
- RBAC案例
- 關聯模型
- 應用開發框架
- 服務端開發
- PC端管理界面開發
- 移動端開發
- 項目實踐
- 失物招領項目
- 移動客戶端UI設計
- 服務端設計
- 數據庫設計
- Event(事件)
- 客戶端設計
- 事件列表頁面
- 發布頁面
- 事件詳情頁面
- API設計
- image
- event
- 微信公眾號開發
- ui設計規范