框架的視圖層由WXMKL(WeiXin Markup language)與WXSS(WeiXin Style Sheet)編寫,由組件進行展示。
對於微信小程序而言,視圖層就是所有.wxml文件與.wxss文件的集合。
微信小程序在邏輯層將數據進行處理后發送給視圖層展現出來,同時接受視圖層的事件反饋。
♦ .wxml文件用於描述頁面的結構。
♦ .wxss文件用於描述頁面的樣式。
視圖層以給定的樣式展現數據並將時間反饋給邏輯層,而數據展現是以組件來進行的。組件(Component)是視圖的基本單元,是構建.wxml文件必不可少的。
對於小程序的WXML編碼開發,我們基本上可以認為就是使用組件、結合時間系統,構建頁面結構的過程。.wxml文件中所綁定的數據,均來自於對應頁的.js文件中Page方法的data對象。
WXML
WXML是框架設計的一套類似HTML的標簽語言,結合基礎組件、事件系統,可以構建出頁面的結構,即.wxml文件。
.wxml文件第一行建議寫<!--頁面名.wxml-->。如:
<!--logs.wxml--> <view class="container log-list"> <block wx:for="{{logs}}" wx:for-item="log"> <text class="log-item">{{index+1}}.{{log}}</text> </block> </view>
通過<view>組件控制頁面內容展現,通過<block>組件與<text>組件實現頁面數據綁定。
WXML具有數據綁定、列表渲染、條件渲染、模板及事件綁定的能力。
數據綁定
.wxml文件中的動態數據均來自對應頁面的.js文件中Page的data對象。
(1)簡單綁定
數據綁定使用Mustache語法(即“雙大括號”語法)將變量包起來。
a.作用於內容
<!--wxml--> <view>{{message}}</view>
//page.js Page({ date:{ message:'Hello MINA!' } })
b.作用於組件屬性
<!--wxml--> <view id="item-{{id}}"></view>
//page.js Page({ data:{ id:0 } })
c.控制屬性
<!--wxml--> <view wx:if="{{condition}}"></view>
//page.js Page({ data:{ condition:true } })
(2)運算
可以在{{}}內進行簡單的運算,支持以下幾種方式:①三元運算,②算數運算,③邏輯判斷,④字符串運算,⑤數據路徑運算
<!--wxml--> <!--①--> <view hidden="{{flag ? true : false}}">Hidden</view> <!--②--> <view>{{a + b}} + {{c}} + d </view> //page.js Page({ data:{ a:1, b:2, c:3 } }) <!--③--> <view wx:if="{{length > 5}}"></view> <!--④--> <view>{{"hello" + name}}</view> //page.js Page({ data:{ name:'MINA' } }) <!--⑤--> <view>{{object.key}}{{array[0]}}</view> //page.js Page({ data:{ object:{ key:'Hello' }, array:['MINA'] } })
(3)組合
可以再Mustache內直接進行組合,構成新的對象或者數組。
數組:
<!--wxml--> <view wx:for="{{zero,1,2,3,4}}">{{item}}</view>
//page.js Page({ data:{ zero:0 } })
最終組合成數組[0,1,2,3,4]。
對象:
<!--wxml--> <template is="objectCombine" data="{{for:a,bar:b}}"></template>
//page.js Page({ data:{ a:1, b:2 } })
最終組合成的對象是{{for:1,bar:2}}。
列表渲染
列表語句可用於.wxml中進行列表渲染,將列表中的各項數據進行重復渲染。
(1)wx:for
在組件上使用wx:for控制屬性綁定一個數組,即可使用數組中各項的數據重復渲染該組件。
默認數組當前項的下標變量名默認為index,數組當前項的變量名默認為item。
<!--wxml--> <view wx:for="{{items}}">
{{index}}:{{item.message}}
</view>
//page.js Page({ data:{ items:[{
message:'foo'
},{
message:'bar'
}] } })
♦ 使用wx:for-item可以指定數組當前元素的變量名。
♦ 使用wx:for-index可以指定數組當前下標的變量名。
<view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName"> {{idx}}:{{itemName.message}} </view>
wx:for也可以嵌套,例如九九乘法表:
<view wx:for="{{[1,2,3,4,5,6,7,8,9]}}" wx:for-item="i"> <view wx:for="{{[1,2,3,4,5,6,7,8,9]}}" wx:for-item="j"> <view wx:if="{{i<=j}}"> {{i}} * {{j}} = {{i*j}} </view> </view> </view>
條件渲染
條件語句可用於.wxml中進行條件渲染,不同的條件進行不同的渲染。
我們用wx:if=“{{condition}}”來判斷是否需要渲染該代碼塊。也可以用wx:elif和wx:else來添加一個else塊。
<!--wxml--> <view wx:if="{{view == 'WEBVIEW'}}">WEBVIEW</view> <view wx:elif="{{view == 'APP'}}">APP</view> <view wx:else="{{view == 'MINA'}}">MINA</view>
//page.js Page({ data:{ view:'MINA' } })
因為wx:if是一個控制屬性,需要將它添加到一個組件標簽上。如果想一次性判斷多個組件標簽,其實可以使用一個<block/>標簽將多個組件包裝起來,並在其上使用wx:if控制屬性。
<block wx:if="{{true}}"> <view> view1 </view> <view> view2 </view> </block>
注:<block/>並不是一個組件,他僅僅是一個包裝元素,不會在頁面中做任何渲染,只接受控制屬性。
wx:if 和 hidden的區別:
因為wx:if之中的模塊也可能包含數據綁定,所以當wx:if的條件值切換時,框架由一個局部渲染的過程,從而確保條件塊在切換時銷毀或重新渲染。
同時wx:if也是惰性的,如果在初始渲染條件為false,框架什么也不做,在條件第一次變成真的時候才開始局部渲染。相比之下,hidden就簡單的多,組件始終會被渲染,只需簡單地控制顯示與隱藏。
一般來說,wx:if有更高的切換消耗,而hidden有更高的初始渲染消耗。因此,如果需要頻繁切換的情況下,用hidden更好;如果運行時條件不大可能改變,則wx:if較好。
模板
WXML支持模板(template),可以在模板中定義代碼片段,然后在不同的地方調用。
使用name屬性,作為模板的名字。使用is屬性,聲明需要使用的模板,然后將模板所需要的data傳入。
<!--wxml--> <template name="staffName"> <view> FirstName:{{firstName}},LastName:{{lastName}} </view> </template> <template is="staffName" data="{{...staffA}}"></template> <template is="staffName" data="{{...staffB}}"></template> <template is="staffName" data="{{...staffC}}"></template>
//page.js Page({ date:{ staffA:{firstName:'Hulk',lastName:'Hu'}, staffB:{firstName:'Shang',lastName:'You'}, staffC:{firstName:'Gideon',lastName:'Lin'}, } })
字例代碼中,“...”為擴展運算符,用來展開一個對象,如staffA對象。
is屬性可以使用Mustache,來動態決定具體需要渲染哪個模板:
<template name="odd"> <view> odd </view> </template> <template name="even"> <view> even </view> </template> <block wx:for="{{[1,2,3,4,5]}}"> <template is="{{item % 2 == 0 ? 'even' : 'odd'}}"> </block>
模板擁有自己的作用域,只能使用data傳入的數據。
事件綁定
事件的定義如下:
♦ 事件是視圖層到邏輯層的通信方式。
♦ 事件可以將用戶的行為反饋到邏輯層進行處理。
♦ 事件可以綁定在組件上,當達到觸發事件,就會執行邏輯層中對應的事件處理函數。
♦ 事件對象可以攜帶額外信息,如id、dataset、touches。
我們小程序與用戶交互,多數情況下是通過事件來進行的。首先,在組件中綁定一個事件處理函數。在下面代碼中,我們使用bindtap,當用戶點擊該組件view時會在該頁面對應Page中找到相應的事件處理函數add。
<!--wxml--> <!--指定view組件的唯一標識tapTest;自定義屬性hi,其值為MINA;綁定事件add--> <view id="tapTest" data-hi="MINA" bindtap="add">{{count}}</view>
注:應將bindtap理解為:bind+tap,即綁定冒泡事件tap(手指觸摸后離開)。
其次,在相應的Page定義中寫上相應的事件處理函數。
//page.js Page({ data:{ count:1 }, add:function(e){ this.setData({ count:this.data.count + 1 }) } })
事件詳解:
微信小程序里的事件分為冒泡事件和非冒泡事件:
♦ 冒泡事件:當一個組件上的事件被觸發后,該事件會向父節點傳遞。
♦ 非冒泡事件:當一個組件上的事件被觸發后,改時間不會向父節點傳遞。
WXML中的冒泡事件僅有6個
除上表之外的其他組件自定義時間都是非冒泡事件。
事件綁定的寫法同組件屬性,以key,value的形式。
♦ key以bind或touch開頭,然后跟上事件的類型,如bindtap、catchtouchstart。
♦ value是一個字符串,需要在對應的Page中定義同名的函數。不然當觸發事件的時候會報錯。
注:bind事件綁定不會阻止冒泡事件向上冒泡,catch事件綁定可以阻止冒泡事件向上冒泡。
看一個例子:
<view id="outter" bindtap="handleTap1"> outer view <view id="middle" catchtap="handleTap2"> middle view <view id="inner" catchtap="handleTap3"> inner view </view> </view> </view>
點擊id為inner的組件會先后觸發handleTap3和handleTap2,不會觸發handleTap1。
如無特殊說明,當組件觸發事件時,邏輯層綁定該事件的處理函數會收到一個事件對象,事件對象具有屬性如下:
BaseEvent 基礎事件對象屬性列表:
CustomEvent 自定義事件對象屬性列表(繼承 BaseEvent):
TouchEvent 觸摸事件對象屬性列表(繼承 BaseEvent):
特殊事件: <canvas/>
中的觸摸事件不可冒泡,所以沒有 currentTarget。
♦ type:通用事件類型。
♦ timeStamp:頁面打開到觸發事件所經過的毫秒數
♦ target:觸發事件的源組件。是一個對象,具有以下三個屬性:
♦ currentTarget:事件綁定的當前組件。與target類似,是一個對象,同樣具有上表三個屬性。
♦ touches:觸摸點數組,每個觸摸點包含以下屬性。
♦ changedTouches:數據格式同touches。表示有變化的觸摸點。
♦ detail:指特殊事件所攜帶的數據。如表單組件的提交事件會攜帶用戶的輸入,媒體的錯誤事件會攜帶錯誤信息。
引用
WXML提供兩種文件引入方式:import和include。
(1)import
import可以在該文件中使用目標文件定義的template,例如:在item.wxml中定義了一個叫item的template:
<!--item.wxml--> <template name="item"> <text>{{text}}</text> </template>
在index.wxml中引用了item.wxml,就可以使用item模板:
<import src="item.wxml"> <template is="item" data="{{text:'forbar'}}"/>
import有作用域,只會引用目標文件中定義的template,而不會引用目標文件嵌套import的template。
(2)include
include可將目標文件除模板代碼(<template/>)塊的所有代碼引入,相當於拷貝到include位置,如:
<!--index.wxml--> <include src="header.wxml"/> <view> body</view> <include src="footer.wxml"/> <!--header.wxml--> <view> header </view> <!--footer.wxml--> <view> footer </view>