上一篇文章我們學習了<list>、<cell>、<loading>和<refresh>組件,這都都是在項目中常用的。那這篇文章我們一起來學習<recycle-list>、<scroller>、<slider>、<indicator>、<textarea>和<input>,大家可能會問爲什麼Weex的組件爲什麼要一個一個學下來呢?其實這是一個很普遍的問題,任何技術不打好基礎,以後實際運用的時候是要買單的,還是那句話,基礎不牢,地動山搖。
那麼我們首先來看一下<recycle-list>組件,這個組件是不是和我們Android中的RecycleView很像,其實他的功能就是一樣的。
1.<recycle-list>的簡介和使用
<recycle-list>
是一個新的列表容器,具有回收和複用的能力,可以大幅優化內存佔用和渲染性能。它只能使用 <cell-slot>
作爲其直接子節點,使用其他節點無效。
<cell-slot>
代表的是列表每一項的模板,它只用來描述模板的結構,並不對應實際的節點。<cell-slot>
的個數只表示模板的種類數,真實列表項的個數是由數據決定的。
屬性 | 說明 |
---|---|
case | 聲明瞭當前模板的類型,只有和數據中的類型與當前類型匹配時纔會渲染,語義和編程語言裏的 case 一致。所有模板中最多隻會匹配到一項,按照模板的順序從上到下匹配,一旦匹配成功就不在繼續匹配下一個。 |
default | 表示當前模板爲默認模板類型,不需要指定值。如果數據項沒有匹配到任何 case 類型,則渲染帶有 default 模板。如果存在多個 default ,則只會使用第一個默認模板。 |
key | 可選屬性,用於指定列表數據中可以作爲唯一標識的鍵值,可以優化渲染性能。 |
<recycle-list>的屬性如下:
for
在<recycle-list>
添加for
屬性即可描述如何循環展開列表的數據,語法和 Vue 的v-for
指令類似,但是它循環的是自己內部的子節點,並不是當前節點。寫法:alias in expression
(alias, index) in expression
switch
在<recycle-list>
添加switch
屬性可以用來指定數據中用於區分子模板類型的字段名,語義和編程語言裏的 switch 一致,配合<cell-slot>
中的case
和default
屬性一起使用。
如果省略了switch
屬性,則只會將第一個<cell-slot>
視爲模板,多餘的模板將會被忽略。
簡單的使用示例如下:
<recycle-list for="(item, i) in longList" switch="type">
<cell-slot case="A">
<text>- A {{i}} -</text>
</cell-slot>
<cell-slot case="B">
<text>- B {{i}} -</text>
</cell-slot>
</recycle-list>
在 <recycle-list>
中使用的子組件也將被視爲模板,在開發組件時給 <template>
標籤添加 recyclable 屬性,纔可以用在 <recycle-list>
中。
<template recyclable>
<div>
<text>...</text>
</div>
</template>
<script>
// ...
</script>
特別說明的是:添加了 recyclable
屬性並不會影響組件本身的功能,它仍然可以用在其他正常的組件裏。
使用<recycle-list>需要注意以下幾點:
1. 屬性和文本的綁定
綁定屬性或者文本時,僅支持表達式,不支持函數調用,也不支持使用 filter,可以參考 Implementation.md#支持的表達式,例如,下列寫法不可用:
<div :prop="capitalize(card.title)">
<text>{{ card.title | capitalize }}</text>
</div>
針對這種場景,推薦使用 computed屬性來實現。因爲模板的取值是由客戶端實現的,而函數的定義在前端(filter 可以認爲是在模板裏調用函數的語法糖),如果每次取值都走一次通信的話,會大幅降低渲染性能
<slot>
不可用<cell-slot>
的功能和 <slot>
有部分重疊,而且更爲激進,在概念上有衝突,存在很多邊界情況無法完全支持。不要在 <cell-slot>
及其子組件裏使用 <slot>
2.
v-once 不會優化渲染性能
和前端框架中的理解不同,客戶端裏要實現複用的邏輯,會標記模板節點的狀態,添加了 v-once
能保證節點只渲染一次,但是並不一定能優化渲染性能,反而可能會拖慢客戶端複用節點時的比對效率。
3. 樣式功能的限制
目前版本里還不支持綁定樣式類名(v-bind:class
),後續會支持的
4. 子組件的限制
-
沒有 Virtual DOM! 使用在
<recycle-list>
中的組件沒有 Virtual DOM!與 Virtual DOM 相關的功能也不支持。在開發過程中儘量只處理數據,不要操作生成後的節點。下列這些屬性都不再有意義,請不要使用:
vm.$refs
裏的值可能是數組、子組件的實例、DOM 元素,在前端裏比較常用,如果不支持,對 Weex 裏的dom
模塊和animation
模塊的功能也有影響。目前正在討論技術方案,部分接口可能會重新設計,或者是在
vm
上透出專爲<recycle-list>
設計的接口。vm.$el
vm.$refs.xxx
vm.$vnode
vm.#slots
vm.#scopedSlots
-
組件的屬性 目前子組件的屬性不支持函數。(正在討論實現方案)
<sub-component :prop="item.xxx" />
因爲子組件的屬性值需要在前端和客戶端之間傳遞,所以僅支持可序列化的值。
item.xxx
的類型可以是對象、數組、字符串、數字、布爾值等,不支持函數。 -
生命週期的行爲差異 由於列表的渲染存在回收機制,節點渲染與否也與用戶的滾動行爲有關,組件的生命週期行爲會有一些不一致。
可回收長列表不會立即渲染所有節點,只有即將滾動到可視區域(以及可滾動的安全區域)內時纔開始渲染,組件生命週期的語義沒變,但是會延遲觸發。
假設有 100 條數據,一條數據了對應一個組件。渲染首屏時只能展示 8 條數據的節點,那就只有前 8 個組件被創建了,也只有前 8 個組件的生命週期被觸發。
- 組件的
beforeCreate
和created
也只有在組件即將創建和創建完成時纔會觸發 - 同理,組件的
beforeMount
和mounted
也只有頁面真正渲染到了該組件,在即將掛載和已經掛載時纔會觸發
- 組件的
-
組件的自定義事件
計劃支持。vm.$on
,vm.$once
,vm.$emit
,vm.$off
等功能還未完全調通,接口可用,但是行爲可能有些差異(參數丟失),暫時不要使用。
使用示例如下:
<recycle-list for="(item, i) in longList" switch="type">
<cell-slot case="A">
<text>- A {{i}} -</text>
</cell-slot>
<cell-slot case="B">
<text>- B {{i}} -</text>
</cell-slot>
</recycle-list>
如果有如下數據:
const longList = [
{ type: 'A' },
{ type: 'B' },
{ type: 'B' },
{ type: 'A' },
{ type: 'B' }
]
則會生成如下等價節點:
<text>- A 0 -</text>
<text>- B 1 -</text>
<text>- B 2 -</text>
<text>- A 3 -</text>
<text>- B 4 -</text>
如果將模板合併成一個,也可以省略 switch
和 case
,將例子進一步簡化:
<recycle-list for="(item, i) in longList">
<cell-slot>
<text>- {{item.type}} {{i}} -</text>
</cell-slot>
</recycle-list>
2.<scroller>的簡介和使用
<scroller>
是一個容納子組件進行橫向或豎向滾動的容器組件。如果你的組件需要進行滾動,可以將 <scroller>
當作根元素或者父元素使用,否則頁面無法滾動( <list>
組件除外, <list>
默認可以滾動)。
有幾點需要開發者特別的注意:
- 不允許相同方向的
<list>
或者<scroller>
互相嵌套,換句話說就是嵌套的<list>
/<scroller>
必須是不同的方向。 <scroller>
需要顯式的設置其寬高,可使用position: absolute;
定位或width
、height
設置其寬高值。
<template>
<scroller class="scroller">
<div class="row" v-for="row in rows" :key="row.id">
<text class="text">{{row.name}}</text>
</div>
</scroller>
</template>
<script>
const dom = weex.requireModule('dom')
export default {
data () {
return {
rows: []
}
},
created () {
for (let i = 0; i < 80; i++) {
this.rows.push({id: i, name: 'row ' + i})
}
},
}
</script>
<scroller>
支持任意類型的 Weex 組件作爲其子組件。 其中,還支持以下兩個特殊組件作爲子組件:
<scroller>的
屬性
參數 | 說明 | 類型 | 默認值 |
---|---|---|---|
show-scrollbar | 控制是否出現滾動條 | boolean | true |
scroll-direction | 控制滾動的方向 | string(horizontal 或者 vertical) | vertical |
loadmoreoffset | 觸發 loadmore 事件所需要的垂直偏移距離(設備屏幕底部與頁面底部之間的距離)。當頁面的滾動條滾動到足夠接近頁面底部時將會觸發 loadmore 這個事件 |
number | 0 |
offset-accuracy | 控制 scroll 事件觸發的頻率,默認值爲 10,表示兩次 scroll 事件之間列表至少滾動了 10px。注意,將該值設置爲較小的數值會提高滾動事件採樣的精度,但同時也會降低頁面的性能 |
number | 10 |
scrollToBegin | 控制 scroll 內容(layout)改變後,是否自動滾到初時位置。默認是true |
string | true |
特別提醒:
scroll-direction
定義了 scroller 的滾動方向,樣式表屬性 flex-direction
定義了 scroller 的佈局方向,兩個方向必須一致。例如:
scroll-direction
的默認值是vertical
,flex-direction
的默認值是column
;- 當需要一個水平方向的 scroller 時,使用
scroll-direction="horizontal"
和flex-direction: row
; - 當需要一個豎直方向的 scroller 時,使用
scroll-direction="vertical"
和flex-direction: column
,由於這兩個值均是默認值,當需要一個豎直方向的 scroller 時,這兩個值可以不設置。
<scroller>有如下四個屬性:
-
loadmore
:如果滾動到底部將會立即觸發這個事件,你可以在這個事件的處理函數中加載下一頁的列表項,可通過loadmoreoffset
屬性設置觸發偏移距離。 -
scroll
:列表發生滾動時將會觸發該事件,事件的默認觸發頻率爲 10px,即列表每滾動 10px 觸發一次,可通過屬性offset-accuracy
設置抽樣率。事件中Event
對象有以下屬性:屬性 說明 類型 contentSize
列表的內容尺寸 Object width
列表內容寬度 number height
列表內容高度 number contentOffset
列表的偏移尺寸 Object x
x 軸上的偏移量 number y
y 軸上的偏移量 number -
scrollstart
:其中H5 暫不支持該事件,當列表開始滾動時觸發,當前的內容高度和列表偏移會在 callback 中返回。 -
scrollend
:其中H5 暫不支持該事件,與scrollstar
類似,當列表結束滾動時觸發,當前的內容高度和列表偏移會在 callback 中返回。
3.<slider>的簡介和使用
Slider 組件用於在一個頁面中展示多個圖片,在前端這種效果被稱爲輪播圖。默認的輪播間隔爲3秒。它支持任意類型的 Weex 組件作爲其子組件。你也可以放置一個 indicator
組件用於顯示輪播指示器。indicator
也只能作爲 Slider
的子組件使用。indicator
不能再包含其它子組件了。
<slider>的屬性有如下幾種:
- auto-play, boolean. 組件渲染完成時,是否自動開始播放,默認爲 false.
- interval, number(ms). 輪播間隔,默認爲 3000ms。
- index, number. 設置顯示slider的第幾個頁面。
- offset-x-accuracy, number. 控制
onscroll
事件觸發的頻率,默認值爲10,表示兩次onscroll
事件之間滾動容器至少滾動了10px。將該值設置爲較小的數值會提高滾動事件採樣的精度,但同時也會降低頁面的性能。 - show-indicators, boolean. 是否顯示指示器。
- infinite, boolean. 設置是否可以無限輪播,默認爲 true。
- scrollable, boolean. 設置是否可以通過滑動手勢來切換頁面,默認爲 true。
- keep-index, boolean, Android. 設置輪播器中的數據發生變化後是否保持變化前的頁面序號。
- forbid-slide-animation, boolean, v0.20+ & iOS. iOS 平臺默認支持動畫,使用該屬性可以強制關閉切換時的動畫。
簡單的使用示例如下所示:
<template>
<div>
<slider class="slider" interval="3000" auto-play="true">
<div class="frame" v-for="img in imageList">
<image class="image" resize="cover" :src="img.src"></image>
</div>
</slider>
</div>
</template>
<style scoped>
.image {
width: 700px;
height: 700px;
}
.slider {
margin-top: 25px;
margin-left: 25px;
width: 700px;
height: 700px;
border-width: 2px;
border-style: solid;
border-color: #41B883;
}
.frame {
width: 700px;
height: 700px;
position: relative;
}
</style>
<script>
export default {
data () {
return {
imageList: [
{ src: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'},
{ src: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'},
{ src: 'https://gd3.alicdn.com/bao/uploaded/i3/TB1x6hYLXXXXXazXVXXXXXXXXXX_!!0-item_pic.jpg'}
]
}
}
}
</script>
4.<indicator>的屬性和使用
indicator
只能在 slider
中使用,說白了就是輪播圖底部的小點,不可添加任何子元素,其中它的樣式也很簡單:
- item-color, color. 指示點默認顏色(未選中態)。
- item-selected-color, color. 指示點選中顏色。
- item-size, number. 指示點半徑。
5.<input>的屬性和使用
Weex 內置的 <input>
組件用來創建接收用戶輸入字符的輸入組件。 <input>
組件的工作方式因 type
屬性的值而異,比如 text
, password
,url
,email
,tel
等。值得注意的是,此組件不支持 click
事件。請監聽 input
或 change
來代替 click
事件,同時也不支持任何子組件。
其屬性可見表格:
key | 類型 | 描述 | 默認值 | 備註 |
---|---|---|---|---|
type |
string | 控件的類型 | text | type 值可以是 text,date,datetime,email, password,tel,time,url,number 。每個 type 值都符合 W3C 標準。 其中,date 和 time 會使用系統默認組件(Android + IOS)查看示例 |
value |
string | 組件的默認內容 | ||
placeholder |
string | 提示用戶可以輸入什麼。 提示文本不能有回車或換行 | ||
autofocus |
boolean | 布爾類型的數據,表示是否在頁面加載時控件自動獲得輸入焦點 | ||
maxlength |
nubmer | 一個數值類型的值,表示輸入的最大長度 | ||
return-key-type |
string | 鍵盤返回鍵的類型(即手機輸入法右下角回車按鈕的地方) |
支持 defalut;go;next;search;send,done 查看示例 |
|
singleline |
boolean | 控制內容是否只允許單行 | true | |
max-length |
number | 控制輸入內容的最大長度 | 字符串長度,即中英文字符長度都爲 1 | |
max |
string | 控制當 type 屬性爲 date 時選擇日期的最大時間,格式爲 yyyy-MM-dd | ||
min |
string | 控制當 type 屬性爲 date 時選擇日期的最小時間,格式爲 yyyy-MM-dd | ||
upriseOffset v0.21+ & iOS |
number | 當鍵盤彈起可能蓋住輸入框時,頁面整體會上移。這個屬性指定鍵盤上邊緣與輸入框下邊緣的間隙。使用 iOS 系統座標,默認是 20。 | 20 | |
hideDoneButton iOS |
number | 隱藏鍵盤上面的完成欄 | false |
其中 placeholder-color {color},placeholder 字符顏色。默認值是 #999999
<input>組件方法:
focus() 將 input
組件聚焦。
blur() 從 input
組件中移除焦點並關閉軟鍵盤(如果它具有焦點)。
setSelectionRange(selectionStart, selectionEnd) ,設置文本選區
key | 類型 | 描述 |
---|---|---|
selectionStart | number | 設置文本選區的起始點 |
selectionEnd | number | 設置文本選區的起終點 |
getSelectionRange(function(params){}) ,獲取文本選區
key | 類型 | 描述 |
---|---|---|
params.selectionStart | number | 文本選區的起始點 |
params.selectionEnd | number | 文本選區的起終點 |
setTextFormatter(params) ,這是一個非常有用的特性,可以對 input 設置一組對輸入的內容進行實時格式化的規則
input標籤使用及事件處理demo:
<template>
<div>
<div class="title">
<text style="text-align: center;font-size: 50px;"><input> Component Demo</text>
</div>
<div class="output">
<scroller>
<text>
{{state}} {{keyboard_state}}
</text>
</scroller>
</div>
<div>
<input test-id="input-obj" ref="range" class="input" type="text" placeholder="input placeholder" @input="eventInput" @change="onChange" @focus="onFocus" @blur="onBlur" @keyboard="onKeyBoard"></input>
</div>
<div class="group">
<div class="panel"><text class="text" @click="setRange">setSelectionRange</text></div>
<div class="panel"><text class="text" @click="getRange">getSelectionRange</text></div>
</div>
</div>
</template>
<script>
module.exports = {
data : {
state:'',
keyboard_state: ''
},
methods : {
eventInput:function (e) {
this.state='input: ' + JSON.stringify(e)
},
onChange:function(e){
this.state='change: '+ JSON.stringify(e)
},
onFocus:function (e) {
this.state='focus: '+ JSON.stringify(e)
},
onBlur:function (e) {
this.state='blur: '+ JSON.stringify(e)
},
onKeyBoard: function(e) {
this.keyboard_state = "\n onkeyboard: " + JSON.stringify(e)
},
setRange() {
this.$refs.range.setSelectionRange(1, 4);
},
getRange() {
this.$refs.range.getSelectionRange(params => {
this.state = `當前文本選區爲 ${params.selectionStart} ~ ${params.selectionEnd}`;
this.keyboard_state = "";
});
},
}
}
</script>
<style scoped>
.mr-base{
margin: 10px;
}
.title {
height: 100px;
margin-top: 30px;
margin-bottom: 20px;
flex-direction: column;
justify-content: center;
}
.output {
border-width: 2px;
border-style: solid;
border-color: rgb(162, 217, 192);
width: 650px;
height: 200px;
margin-left: 50px;
margin-top: 20px;
margin-bottom: 30px;
}
.desc{
color:#aa0000;
font-size: 30px;
}
.input{
width: 650px;
height:100px;
border-width: 1px;
border-style: solid;
border-color: rgb(162, 217, 192);
placeholder-color: #41B883;
margin-left: 50px;
padding-left: 20px;
font-size: 28px;
}
.panel {
height: 100px;
flex-direction: column;
border-width: 2px;
border-style: solid;
border-color: rgb(162, 217, 192);
background-color: rgba(162, 217, 192, 0.2);
margin-left: 20px;
display:inline-block;
padding: 10px;
padding-top: 20px;
}
.group {
flex-direction: row;
width:auto;
margin-top: 30px;
margin-left: 45px;
margin-right: auto;
}
</style>
6.<textarea>的屬性和使用
<textarea>
與 <input>
組件類似,可用於接受用戶輸入數據。<textarea>
支持多行文本輸入。 <textarea>
支持 <input>
支持的所有的屬性、樣式和事件,不支持子組件。
除了支持 input
支持的所有屬性外,textarea
還支持 row
屬性,用於指定輸入的行數。
- row, number, 默認值爲2。
<textarea>的事件:
- 通用事件 支持所有通用事件。
- input. 當輸入狀態時,會不斷觸發。
- @param value: 當前文本。
- change. 當用戶完成了輸入時觸發。
- @param value: 當前文本。
- focus. 當輸入框獲得焦點時觸發。
- blur. 當輸入框失去焦點時觸發。
- return. 當用戶點擊了“回車”按鈕時觸發,會返回此時“回車”按鈕的動作類型。
- @param value: 當前文本。
- @param returnKeyType, "default" | "go" | "next" | "search" | "send" | "done".
- keyboard. 當鍵盤彈起或收起時觸發。
- @param isShow: boolean, 顯示或隱藏鍵盤。
- @param keyboardSize: 鍵盤的高度,以前端使用的樣式單位返回。
其示例:
<template>
<div class="wrapper">
<textarea class="textarea" @input="oninput" @change="onchange" @focus="onfocus" @blur="onblur"></textarea>
</div>
</template>
<script>
const modal = weex.requireModule('modal')
export default {
methods: {
oninput (event) {
console.log('oninput:', event.value)
modal.toast({
message: `oninput: ${event.value}`,
duration: 0.8
})
},
onchange (event) {
console.log('onchange:', event.value)
modal.toast({
message: `onchange: ${event.value}`,
duration: 0.8
})
},
onfocus (event) {
console.log('onfocus:', event.value)
modal.toast({
message: `onfocus: ${event.value}`,
duration: 0.8
})
},
onblur (event) {
console.log('onblur:', event.value)
modal.toast({
message: `input blur: ${event.value}`,
duration: 0.8
})
}
}
}
</script>
<style>
.textarea {
font-size: 50px;
width: 650px;
margin-top: 50px;
margin-left: 50px;
padding-top: 20px;
padding-bottom: 20px;
padding-left: 20px;
padding-right: 20px;
color: #666666;
border-width: 2px;
border-style: solid;
border-color: #41B883;
}
</style>
至此<recycle-list>、<scroller>、<slider>、<indicator>、<textarea>和<input>這六個組件就學習完了,學起來是不是覺得和Android的組件極度相似?所以在學起來是沒有任何難度的。那麼下篇博客我們將學習Weex剩下的四個常用組件,不積跬步無以至千里,新技術的路上,加油!