《微信小程序七日談》系列文章:
- 第一天:人生若只如初見;
- 第二天:你可能要拋棄原來的響應式開發思維;
- 第三天:玩轉Page組件的生命周期;
- 第四天:頁面路徑最多五層?導航可以這么玩;
- 第五天:你可能要在登錄功能上花費大力氣;
- 第六天:小程序devtool隱藏的秘密;
- 第七天:不要撿了芝麻丟了西瓜
本系列的文章並非初學教程,而是筆者在具體開發過程中遇到的問題以及部分解決方案。
微信小程序自公布以來就被捧上了天,新聞一波接一波。一部分聲音來自前端界,把小程序當成前端歷史地位提升的一次革新;一部分聲音來自app開發界,把小程序當成失業的助推器(攤手)。本文的目的不是討論小程序的是或非,只是記錄一下筆者在開發小程序過程中一些收獲和感想。
有消息稱第一批微信小程序在12月中下旬發布,在那之前,需要將已完成的小程序向騰訊提交審核。58到家看准了這次推廣的機會,制定了小程序開發計划。筆者是主要開發者之一。
閑話少說,進入正題。
小程序號稱使用前端技術開發接近native體驗的webapp,微信提供了許多js和native交互的bridge API,同時將html/css進行改造,分別對應wxml和wxss。初見之時,看上去就是換個名字而已嘛,都是熟悉的技術,項目分分鍾開發完成哈哈。然后就興致勃勃的開始折騰,然后就...
小程序官方文檔相當“簡潔”,以示例代碼的形式很形象地說明了各模塊的開發模式。但是示例代碼以及文字描述並未將其中的細節完全暴露出來,上手開發后才發現很多坑。
wxss
wxss乍看上去就是css,名字相似,語法相似。但寫起代碼來真是痛苦的很,下面一一列出目前筆者遇到的問題。
1> 不支持級聯選擇器
css選擇器可以支持自上而下一層層的級聯選擇,比如:
.parent .child{
color: #000;
}
前端開發者對此非常熟悉。但是wxss並不支持上述語法,如果使用class作為匹配選擇器,只能寫一層,如下:
.parent{}
.child{
color: #000;
}
這種模式令開發者在為class命名上必須不能重復,限制了自由度。
注意:目前最新版的小程序已經支持級聯選擇器。
2> 選擇器支持非常有限
目前官方給出的wxss支持選擇器只有以下幾種:
其中的element
是wxml支持的標簽元素,並非所有html標簽。
3> 不支持引用本地圖片資源
比如我們需要使用本地的sprites圖片:
.dj__icon {
background-image; url("./assets/icons.sprites.png");
}
如果在wxss中編寫以上代碼並不會報錯,但是也不會有任何理想的效果。官方給出的答復是:
所以如果我們需要使用自定義的圖標UI的話,目前只能先將sprites圖片上傳到自己的服務器或者base64編譯后再寫入wxss中。
wxml
wxml的語法與vue.js有點相似,支持數據綁定以及部分模板邏輯。筆者目前在wxml開發中總結以下幾點注意事項:
**1> 使用wxml模板語法時只能使用部分js邏輯判斷 **
或者也可以理解為只能使用以下幾種邏輯:
- 引用變量;
- 二元操作符;
- 三元操作符。
由於wxml使用雙花括號{{}}
作為數據綁定標識,所以被{{}}
包裹的邏輯語句不能出現花括號{}
,否則會報語法錯誤。比如:
<view>{{ Object.assign({},data,{isTrue: false})}}</view>
2> event handler的參數event
不支持訪問DOM
小程序中不支持任何訪問DOM的語法,因為它並不是在瀏覽器環境下運行,所以document
、window
等瀏覽器暴露的API均不能訪問。事件響應函數接受的event
參數的完整結構如下:
{
"type":"tap",
"timeStamp":895,
"target": {
"id": "tapTest",
"dataset": {
"hi":"WeChat"
}
},
"currentTarget": {
"id": "tapTest",
"dataset": {
"hi":"WeChat"
}
},
"detail": {
"x":53,
"y":14
},
"touches":[{
"identifier":0,
"pageX":53,
"pageY":14,
"clientX":53,
"clientY":14
}],
"changedTouches":[{
"identifier":0,
"pageX":53,
"pageY":14,
"clientX":53,
"clientY":14
}]
}
所以如果想使用常規瀏覽器環境下,通過event.target
獲取DOM是不可行的。只能通過操作數據來修改UI。
3> 使用剩余參數語法向模板傳遞對象格式的data
wxml支持模板引用以便開發通用組件,比如項目中存在item.wxml
:
<!-- item.wxml -->
<template name="item">
<text>{{text}}</text>
</template>
在 index.wxml
中引用了 item.wxml
,就可以使用item模板:
<import src="item.wxml"/>
<template is="item" data="{{text: 'forbar'}}"/>
其中的data
是向item.wxml
傳遞的參數。如果要循環一個列表數據,列表中每個元素都是key-value格式且對應一個item的話,需要使用以下格式:
<import src="item.wxml"/>
<template is="item" wx:for="{{list}}" wx:for-item="item" data="{{..item}}"/>
上述代碼將列表的每個元素整體傳遞給item.wxml
,使用 data="{{..item}}"
語法。當然,你也可以在data中添加其他數據,比如 data="{{..item, text:'forbar'}}"
。
3> 可將事件響應函數名稱通過data
傳遞給組件模板
組件對引用它的模板來說就是一個黑盒,外部不應該干涉組件的內部邏輯。如果想要組件響應某些操作並反饋外部定義的響應函數,可以將外部已定義的響應函數名稱傳給組件。
我們將上文的代碼改造一下演示這個問題。比如item.wxml
暴露了tap響應API:
<!-- item.wxml -->
<template name="item">
<text bindtap="{{tapHandler}}">{{text}}</text>
</template>
index.wxml
中引入item組件並傳遞了tapHandler:
<import src="item.wxml"/>
<template is="item" data="{{text: 'forbar', tapHandler: 'indexTapHandler'}}"/>
其中indexTapHandler
是index.wxml
對應的js中聲明的響應函數:
Page({
indexTapHandler(e){
alert('trigger index tap event');
}
});
這樣就完成了組件事件響應的定制。
3> 可變數組數據渲染務必使用wx:key
渲染列表數據時,如果列表中的數據是動態的(比如點擊之后修改列表中某個元素的值),那么在渲染UI時務必將渲染的模板加上wx:key
。請看以下代碼:
<import src="item.wxml"/>
<template is="item" wx:for="{{list}}" wx:for-item="item" wx:for-index="idx" wx:key="item-{{idx}}" data="{{..item}}"/>
上述代碼使用列表的index作為wx:key
的值,保證每個元素對應template的唯一性。
wx:key
的取值還可以是保留字*this
,官方給出的定義如下:
保留關鍵字 *this 代表在 for 循環中的 item 本身,這種表示需要 item 本身是一個唯一的字符串或者數字。
根據以上的解釋,如果item本身是動態不唯一的,是不能使用*this
作為wx:key
取值的,否則會出現非常詭異的情況。
總結
第一天的開發感想就是:小程序並不像第一眼看上去那么美好,不能把小程序簡單地理解為html+css+js。本文暫時不討論小程序背后的運行原理,單純出開發層面看,wxss相當於極度縮減版的css;wxml是mustache的簡化版。至於js,后續筆者會將遇到的問題和經驗寫出來,以供參考。