小程序开发日记
這里主要記錄這段時間開發小程序過程中遇到的坑和要注意的點。主要是希望在以后開發小程序的過程中能在評審需求的時候就發現哪些能實現,哪些實現起來比較困難。這樣就能在定設計和交互之前盡可能的減少后期踩坑的風險。
下拉刷新
自定義的下拉刷新
微信自帶的下拉刷新需要在配置中開啟:
| backgroundTextStyle | string | dark | 下拉 loading 的樣式,僅支持 dark / light |
| enablePullDownRefresh | boolean | false | true 開啟 false關閉 |
這里有個要注意的問題:
在onPullDownRefresh中不能調用wx.startPullDownRefresh(),否則會死循環
自定義的下拉刷新,目前只支持整個頁面的下拉刷新,對于局部的下拉刷新,就沒有辦法了:
局部的下拉刷新
這種我們可以使用自定義下拉刷新的方式來實現。詳細的可以看我的另一篇博客,這里也就不贅述了。
關于自定義頂部navigationBar
小程序自帶的navigationBar僅可以在json文件中配置。提供的可配置項也是少的可憐。主要如下:
| navigationBarBackgroundColor | HexColor | #000000 | 導航欄標題文字內容 |
| navigationBarTextStyle | string | white | 導航欄標題顏色,僅支持 black / white |
| navigationBarTitleText | string | 導航欄標題文字內容 |
從上面的表格可以看出來,頂部導航欄樣式固定,我們僅可以修改文字,字體顏色和背景色。
不過還好小程序還提供了自定義頂部導航的配置,window.navigationStyle,這個配置項支持兩個值default|custom。默認是default,表示的是用小程序自帶的導航欄,配置為custom時是自定義導航欄,其實就是小程序隱藏掉導航欄,然后我們自己實現。
但是自定義導航欄不僅帶來了設計上的自由,也帶來了很多坑。
下拉刷新
不同于web開發,小程序和app對于下拉刷新的需求非常的多,也算是基本功能之一,小程序本身也自帶下拉刷新。我們只需將window.enablePullDownRefresh設置為true,然后在頁面監聽onPullDownRefresh即可。
但是當我們用到自定義navigationBar的時候,會發現,本來fixed定位在頂端的navigationBar會被一起拉下來。
系統自帶的navigationBar的下拉刷新
自定義navigationBar的下拉刷新
這個時候我們就必須要使用自定義的下拉刷新。關于自定義的下拉刷新的實現原理這里就不多說了。但是實現起來在真機中發現,android手機會有明顯的卡頓。
層級問題
按照正常的navigationBar,一般這個組件的層級是最高,僅次于遮罩層和彈窗這些組件。一般的組件可以使用z-index屬性來控制層級。
但是小程序中有種概念叫做:原生組件
這些組件包括camera,canvas,input(僅在focus時表現為原生組件),map,textarea,video,live-player,live-pusher
這種組件脫離于WebView渲染流程之外,層級也是最高的,因此無論z-index設置多大,都無法覆蓋原生組件。
系統自帶的navigationBar
自定義navigationBar
雖然理論上我們可以用cover-view和cover-image來實現自定義的navigationBar,但是個人覺得還是盡量避免使用自定義的navigationBar。
雖然cover-view和cover-image組件可以覆蓋在部分原生組件上面。對于原生組件之間,可以使用z-index來控制他們的層級。
但是小程序cover-view組件內部只支持嵌套cover-view和cover-image以及button。這在很大程度上不支持我們作出多么有個性化的組件。而且實現起來坑也很多。
而且關于用z-index來控制層級這點也存疑,雖然文檔上這樣說明,但是,我在開發中發現,實際上還是看渲染的順序。后渲染的始終在先渲染的上層。
鍵盤彈起時會將頂部導航欄頂上去
這個是針對textarea組件在頁面底部的時候,準確來說是textarea組件距離底部的距離沒有鍵盤高的時候。在鍵盤彈起時造成了整個頁面上移,從而導致了導航欄會移到頁面外。
當鍵盤未彈起時
當鍵盤彈起時
當然這個不算是硬傷,畢竟出現的條件有限,我們可以在設計上盡量避免將textarea放到底部來避免這個坑。
自定義底部tabbar
自定義的tabbar
在說自定義之前先看看小程序自帶的tabbar可以做到什么程度。
自定義的tabbar是在app.json中配置的,在tabBar下:
- 基本配置
| color | HexColor | tab 上的文字默認顏色,僅支持十六進制顏色 |
| selectedColor | HexColor | tab 上的文字選中時的顏色,僅支持十六進制顏色 |
| backgroundColor | HexColor | tab 的背景色,僅支持十六進制顏色 |
| borderStyle | string | tabbar上邊框的顏色, 僅支持 black / white |
| list | Array | tab 的列表,詳見 list 屬性說明,最少2個、最多5個 tab |
| position | string | tabBar的位置,僅支持 bottom / top |
| custom | boolean | 自定義 tabBar,見詳情 |
- list選項配置
| pagePath | string | 頁面路徑,必須在 pages 中先定義 |
| text | string | tab 上按鈕文字 |
| iconPath | string | 圖片路徑,icon 大小限制為40kb,建議尺寸為 81px * 81px,不支持網絡圖片。當 position 為 top 時,不顯示 icon。 |
| selectedIconPath | string | 選中時的圖片路徑,icon 大小限制為40kb,建議尺寸為 81px * 81px,不支持網絡圖片。當 position 為 top 時,不顯示 icon。 |
所以從上面我們可以看到,我們可以定義tabbar的選中和未選中圖標和字體顏色。沒辦法加入別的樣式和嵌入別的自定義點擊事件。
自定義的幾種實現方式
這是比較老的版本的形式。在需要tabbar的頁面嵌入tabbar組件。這是最簡單的實現方式。但是在首次切換的時候,會有很明顯的閃屏。
這個比第一種要好點,也是我在項目中用到的一種模式,但是切換的時候也有稍微的閃屏
這種實現方式稍微復雜,也就是將首頁的幾個頁面作為組件傳入,通過路由控制頁面切換。
之所以叫偽tabbar的形勢,是因為這個只是表面上是tabbar。
理論上這種方式實現的在切換的時候可以做到不閃屏。但是會不會帶來別的問題呢?
比如說返回的時候會不會造成頁面錯亂?
原本的頁面生命周期和組件的生命周期略有不同,會不會造成一些坑?
還有個幾乎可以肯定的問題,就是如果不使用cover-view的話,我們就沒法蓋住原生組件。
不過好在小程序組件和頁面之間的切換很方便,特別是在用Taro之后,組件和頁面的區分僅僅只是是否在app.tsx中注冊。所以第二和第三種實現方式切換起來并不是很麻煩。但是目前看來的話第二種實現方式體驗還算滿意,因此也沒有必要切換到第三種方式。
關于彈窗
額,其實我說的這三個,幾乎可以總結出一個問題,那就是小程序中讓人吐血的層級問題。
其實不論是彈窗還是navigation還是tabbar他們都有一個特點,就是定位在頁面的某一個位置,還有層級要足夠高,要能夠覆蓋住底層元素。
官方沒有專門的彈窗容器(我覺得應該有一個彈窗容器)因此只能靠我們自己寫了。但是因為cover-view令人蛋疼的樣式支持度,個人覺得僅僅用cover-view和cover-image來實現一個定制化的彈窗幾乎不可能。
如果不用cover-view你會發現很多常用的組件都是騎在你臉上,而你毫無辦法的。
因此個人建議,在有原生組件的頁面上,盡量避免彈層的出現。
如果是在無法妥協,那也建議彈窗組件分兩塊來寫,一種專門用cover-view和cover-image來寫,并且一定要寫z-index來控制層級,理論上是后面的元素會覆蓋在上一個元素上面,但是還是要防止有些組件在操作的過程中重新渲染,而改變原有的層級。而對于頁面中沒有原生組件的,可以用view來寫,這樣樣式上就自由很多。
關于html2wxml
關于富文本的渲染。現在基本的做法都是先把html解析為節點信息,然后再通過模板渲染為wxml。但是因為小程序模板不支持遞歸調用。所以在很多第三方組件中都出現以下的代碼:
<!--temp0--> <template> ...<template is="temp1"></template> </template> <!--temp1--> <template> ...<template is="temp2"></template> </template> <!--temp2--> <template> ...<template is="temp3"></template> </template> ... 復制代碼通常這種代碼會出現十幾到二十幾個,也就是說最多支持嵌套二十多層。如果不夠的話就得自己加了。我就遇到過一個富文本,足足嵌套到了兩百多層。我復制到一百的時候實在受不了了,寫了一個模板生成器來完成。
網上有人說這種代碼看起來蠢哭了。的確,但是也很無奈。
時間格式化問題
這個不能說是小程序的坑,應該說是ios和android對new Date()處理上的差異。我們可以用safari瀏覽器和chrome來復現這兩種差異
我們公司前后端交互用的時間格式是YYYY-MM-DDTHH:mm:ss。這種格式的時間字符串用new Date()來處理,在safari和chrome的表現如下:
new Date('2019-05-29T14:00:00') // safari Wed May 29 2019 22:00:00 GMT+0800 (CST) = $2 // chrome Wed May 29 2019 14:00:00 GMT+0800 (中國標準時間) 復制代碼safari是比chrome要早8小時的,這是因為chrome認為這個時間是本地時間,而safari認為是國際標準時間,所以會有這樣的8小時差異(僅限于中國)。
因此在調用new Date()之前我們需要把YYYY-MM-DDTHH:mm:ss格式的轉換為YYYY/MM/DD HH:mm:ss這種格式的字符串。
此外,我在處理的過程中還發現帶毫秒數的事件字符串2019-05-29T14:00:00.000,這種的還需要將毫秒數去掉變成這種格式2019/05/29 14:00:00。然后在ios和android上表現也就一致了。
function getDate(date: any) {if(typeof date === 'string') {return new Date(date.replace('T', ' ').replace(/\-/g, '/').split('.')[0])}return new Date(date) } 復制代碼字體
小程序在android下,字體的font-weight必須要設置到700及以上才會變粗,或者統一使用bold
ios
android
小程序分包
小程序大小是有限制的,目前是主包不超過2M。可是為了實現一些功能,導致我們很容易就超過了這個限制。
好在官方提供了分包方式。具體可以參照官方文檔。
就一點:對于副包內引用的,較大的包,應該包含在分包的文件夾內部。不然仍然會打包在主包內部
關于px和rpx
不能無腦的全站用rpx來做適配
最近在做一個需求,覺得有個點還是需要注意的,特此記錄一下。
這個需求就是一個簡單的消息輪播。如下圖:
就是紅框區域的一個向下無限滾動輪播,時間間隔為2s。我是使用translateY來實現的。每次translateY的高度和消息塊的高度相同。
但是在滾動的過程中,發現在某些機型上面每次滾動都會有細微的偏移,而在某些機型上面正常。
消息顯示窗口的高度和每個消息的高度都是80rpx。代碼如下:
<View className="notice-pannel" style={{transform: `translateY(${curIndex * -80}rpx)`, transition}}> {list.map(item => (<View className="notice-item" key={item.id}><Image className="notice-avatar" src={item.actor.avatar_url}></Image><Text className="notice-desc">{`${item.actor.login} ${item.payload.action} ${item.repo.name} at ${new DateX(item.created_at).format()}`}</Text></View>)) } </View> 復制代碼最后定位問題的原因是因為部分屏幕寬度在換算rpx的時候會有誤差,導致每次translateY的時候會有一個小誤差。
rpx根據官方文檔的定義是:rpx(responsive pixel): 可以根據屏幕寬度進行自適應。規定屏幕寬為750rpx。
因此,會有些屏幕寬度在rpx轉px的時候會有除不盡的時候,這時候往往會有四舍五入取整的情況。
在看這行代碼${curIndex * -80}rpx),假設我們的設備的屏幕寬度為412這時候80rpx專成px的時候是43.946666666666665px。開發者工具上會發現,實際上是換算成了43,也就是每個消息塊的高度是43px。
當curIndex為2的時候,實際上我們應該偏移43 * 2也就是86px,可是如果是直接用rpx的話,我們發現會是87px。這時候就產生了偏移了。
知道原因的話解決方案也有了,就是用px做單位就可以了
<View className="notice-pannel" style={{transform: `translateY(${curIndex * rpx2px(-80)}rpx)`, transition}}> {list.map(item => (<View className="notice-item" key={item.id}><Image className="notice-avatar" src={item.actor.avatar_url}></Image><Text className="notice-desc">{`${item.actor.login} ${item.payload.action} ${item.repo.name} at ${new DateX(item.created_at).format()}`}</Text></View>)) } </View> 復制代碼轉載于:https://juejin.im/post/5cf4861d6fb9a07efa090237
總結
- 上一篇: 【更新链接】U盘启动制作工具(UDTOO
- 下一篇: shell date