天天看點

Weex内置元件的使用——Weex的學習之路(四)

上一篇文章我們學習了<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 元件作為其子元件。 其中,還支援以下兩個特殊元件作為子元件:

  • <refresh>

    : 用于添加下拉重新整理的功能。詳情請檢視

    <refersh>

  • <loading>

    : 用于添加上拉加載更多的功能。詳情請檢視

    <loading>

<scroller>的

屬性

參數 說明 類型 預設值
show-scrollbar 控制是否出現滾動條 boolean true
scroll-direction 控制滾動的方向 string(horizontal 或者 vertical) vertical
loadmoreoffset 觸發

loadmore

事件所需要的垂直偏移距離(裝置螢幕底部與頁面底部之間的距離)。當頁面的滾動條滾動到足夠接近頁面底部時将會觸發

loadmore

這個事件
number
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 鍵盤傳回鍵的類型(即手機輸入法右下角回車按鈕的地方)
Weex内置元件的使用——Weex的學習之路(四)

支援 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;">&lt;input&gt; 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剩下的四個常用元件,不積跬步無以至千裡,新技術的路上,加油!