小程序以免安裝用完即走的特性自發(fā)布初就很火,即使是現(xiàn)在也是熱度不減。小程序雖然是一個(gè) HTML5,但是通過限制開發(fā)者的寫法,提供一套自定義的組件以及寫法,并且將一部分耗費(fèi)性能的組件使用客戶端渲染來極大的提高網(wǎng)頁的性能。
前段時(shí)間我們也進(jìn)行了小程序的開發(fā)。由于是接手的項(xiàng)目,這個(gè)項(xiàng)目之前是沒有使用框架,直接使用原生小程序開發(fā)的,開發(fā)過程中就發(fā)現(xiàn)有很多不方便的地方。例如 NPM, Promise 組件化等。最后我對(duì) wepy , tinajs , labrador , mpvue 等多個(gè)框架進(jìn)行了調(diào)研,最后決定使用 tinajs 對(duì)項(xiàng)目進(jìn)行重構(gòu)。選擇 tinajs 的原因是它是基于小程序的語法,并沒有多大的改造,學(xué)習(xí)成本非常低。同時(shí)它的漸進(jìn)式概念讓我能夠循序漸進(jìn)的接入框架,而不是一下子就要把它的所有概念都要學(xué)習(xí)下來,這對(duì)當(dāng)時(shí)工期比較緊張的我來說是比較重要的。當(dāng)然還有一個(gè)更重要的原因是,它的源碼非常的簡(jiǎn)單,即使是出了問題我也能快速定位出來。
不過即使是選擇了一套正確的框架,碰到坑的情況也是在所難免的,小程序更甚。下面我就我這段時(shí)間碰到的一些比較經(jīng)典的問題來說一下我碰到的一些問題。
我們是一個(gè)新聞流的項(xiàng)目,用戶可以無限下拉加載數(shù)據(jù),內(nèi)部會(huì)使用一個(gè)數(shù)組將列表的數(shù)據(jù)存儲(chǔ)起來。當(dāng)我美滋滋的使用 tinajs 重構(gòu)完項(xiàng)目后準(zhǔn)備試試的時(shí)候,我就發(fā)現(xiàn),當(dāng)我加載數(shù)據(jù)超過一定數(shù)量限制(大概200條數(shù)據(jù))之后,控制臺(tái)就會(huì)報(bào)“輸出傳輸長度超過最大長度”的錯(cuò)誤。
最后查了一下發(fā)現(xiàn)是小程序?yàn)榱诵阅芟拗屏艘淮武秩緜鬏數(shù)臄?shù)據(jù)量大小。最開始我以為是我的數(shù)據(jù)超過限制了,所以采取了精簡(jiǎn)不必要字段的辦法將數(shù)據(jù)體積減小。不過在我縮減到不能再縮減的時(shí)候,發(fā)現(xiàn)依舊沒什么卵用。后來就想到我的數(shù)據(jù)真的有這么大么,于是乎拿之前原生的測(cè)試了一下,發(fā)現(xiàn)刷到七百多條數(shù)據(jù)的時(shí)候也會(huì)提示這個(gè)錯(cuò)誤。那么就可以證明我之前兩百條數(shù)據(jù)的時(shí)候沒有超過限制,同時(shí)我將數(shù)據(jù) JSON 化并保存到本地文件里查看了一下,發(fā)現(xiàn)才150KB左右,遠(yuǎn)遠(yuǎn)沒有達(dá)到上限。
最后經(jīng)過我和 tinajs 作者的逐一分析,發(fā)現(xiàn)可能是自定義組件的鍋。因?yàn)槲业牧斜碓赜胁煌臉邮剑晕沂褂昧俗远x組件去定義了不同的樣式類型組件,部分組件又有公共的部分所以又要抽離出來變成組件,也就是說實(shí)際上我的列表是由一個(gè)多層嵌套的自定義組件循環(huán)渲染而成的。我們猜測(cè)最后小程序渲染的時(shí)候,每一個(gè)自定義組件傳入的數(shù)據(jù)都會(huì)做一次拷貝,這樣就導(dǎo)致了我本來 150K 的數(shù)據(jù),瞬間就超過了它們的限制。
最后解決的辦法也非常簡(jiǎn)單,由于我其實(shí)大多數(shù)都是純渲染的組件,所以組件內(nèi)部的自定義組件我都是用 <template> 模板去渲染,這種情況下不會(huì)觸發(fā)數(shù)據(jù)的拷貝試了下就沒有問題了。當(dāng)然除了我說的減少數(shù)據(jù)體積以及是用自定義模板代替自定義組件減少數(shù)據(jù)拷貝層級(jí)之外,我們還可以對(duì) 數(shù)據(jù)進(jìn)行分頁操作 來達(dá)到減少一次數(shù)據(jù)渲染的體積。
我們的小程序有一個(gè)下拉刷新的功能,小程序自己官方是有封裝 onPullDownRefresh 接口來幫助我們完成這個(gè)事。不過因?yàn)槲覀兊南吕⑿率怯凶远x樣式的,所以就沒辦法使用官方的接口了。最后做出來的效果大概如下所示:
最開始我是使用了 <scroll-view> 組件來做滾動(dòng),同時(shí)使用 scrolltoupper 來觸發(fā)下拉的事件。內(nèi)部則是使用了 translate 操作來展開下拉卡片。一頓操作之后覺得甚是完美,但是之后突然發(fā)現(xiàn)官方提示:
請(qǐng)勿在 scroll-view 中使用 textarea 、 map 、 canvas 、 video 組件
因?yàn)檫@幾個(gè)組件都是使用 Native 實(shí)現(xiàn)的,只能是固定在屏幕上的存在,所以沒辦法在 scroll-view 中使用。因?yàn)橹罂赡軙?huì)在里面加入視頻的數(shù)據(jù),所以對(duì)這個(gè)組件就有點(diǎn)望而卻步。同時(shí)使用了這個(gè)組件之后,外部的其它組件想要修改 scrllTop 的話會(huì)變得很麻煩,都需要自維護(hù)一套事件,增加了業(yè)務(wù)的復(fù)雜度。
最終我退回成普通的 view 監(jiān)聽 touchstart , touchmove 和 touchend 事件,根據(jù)移動(dòng)的距離來判斷下拉百分比來實(shí)現(xiàn)這個(gè)功能。最終的實(shí)現(xiàn)可以說是異常艱辛的。不過這個(gè)實(shí)現(xiàn)完了之后,又出現(xiàn)了一個(gè)問題。在 iOS 中會(huì)存在阻尼效果,也就是下拉的時(shí)候滾動(dòng)條會(huì)有一個(gè)回彈的特效,導(dǎo)致你雖然下拉了但是 touch 事件并沒辦法有效的執(zhí)行。目前這個(gè)問題還沒有比較好的解決辦法,這里也有用戶提出了需要 提供禁止頁面阻尼效果的參數(shù) ,不過目前還沒有官方回應(yīng)。
除了阻尼問題之外,還有一個(gè)問題是 wx.pageScrollTo() 方法提供了 duration 參數(shù),讓滾動(dòng)能夠有動(dòng)畫效果。你可以在開發(fā)者工具中看到,實(shí)際上小程序的這個(gè)動(dòng)畫是使用 transform 屬性來做的。正常情況來說這個(gè)是沒有問題的,但是對(duì)于頁面內(nèi)存在 position: fixed 的元素來說,這個(gè)是有問題的。為什么這么說呢,大家可以看看 MDN 上 fixed 的描述:
It is positioned relative to the initial containing block established by the viewport, except when one of its ancestors has a transform , perspective , or filter property set to something other than none (see the CSS Transforms Spec), in which case that ancestor behaves as the containing block.
via: https://developer.mozilla.org/en-US/docs/Web/CSS/position
文檔上說 fixed 默認(rèn)是相對(duì)于視口的,除非說父級(jí)元素設(shè)置了 transform , perspective 或者 filter 數(shù)值的值為非 none ,那么就會(huì)相對(duì)于這個(gè)祖先元素。這樣就造成了如果我最開始相對(duì)于窗口設(shè)置了一個(gè)元素 fixed 在右下角,當(dāng)我 wx.pageScrollTo() 操作的時(shí)候本來相對(duì)于窗口的元素就會(huì)突然相對(duì)于 <page> 定位,當(dāng)滾動(dòng)結(jié)束之后因?yàn)?nbsp;transform 屬性已經(jīng)消失,所以元素又會(huì)閃現(xiàn)回來。有做一個(gè)代碼片段,大家感興趣的也可以試試:wechatide://minicode/Q7zHi6m96eYV 。滾到底部之后點(diǎn)擊下方的藍(lán)色色塊,會(huì)發(fā)現(xiàn)藍(lán)色色塊出現(xiàn)閃動(dòng),原因就是剛才描述的。解決辦法就是去掉 wx.pageScrollTo() 的 duration 參數(shù),或者是將滾動(dòng)內(nèi)容使用 <scroll-view> 包裹與 fixed 元素分離。
除了以上兩個(gè)問題之外,還有一個(gè)就是 Canvas 畫布的問題。這個(gè) Canvas 畫布最大的問題在于小程序內(nèi)部是使用客戶端組件實(shí)現(xiàn)的,但是在開發(fā)者工具中由于是網(wǎng)頁預(yù)覽所以這里的是 HTML 中的 <canvas> 。雖然微信將 HTML 原生的 canvas 大部分接口都實(shí)現(xiàn)了,但是我要說...還是有!很多!不!一!樣!敲黑板,劃重點(diǎn)!所以這就導(dǎo)致了開發(fā)者工具上看到的效果和客戶端實(shí)際看到的效果有可能會(huì)完全不一樣,給我們開發(fā)過程帶來了無盡的阻撓!而且我這邊不知道為什么,即使是同樣身為客戶端版本的調(diào)試版,預(yù)覽版,線上版,這三者有時(shí)候表現(xiàn)出來的行為也可能造成不一樣,我當(dāng)時(shí)的內(nèi)心真的是萬馬奔騰!
由于是客戶端渲染的畫布,所以小程序的畫布有以下幾個(gè)比較明顯的特點(diǎn):
另外還有一個(gè)問題在于,小程序的畫布必須可視才能繪制成功,也就是說如果你給這個(gè)畫布設(shè)置 display:none 然后等它繪制成功之后再顯示出來是不可以的。目前我的解決辦法是在頁面用戶不可視區(qū)域內(nèi)先繪制然后再獲取圖片內(nèi)容。
目前接觸到的小程序的一些問題大概是這么多,有些可能在之后的版本中會(huì)解決(例如阻尼效果),而有些真的就是特性必須去適應(yīng)(例如畫布)。希望我總結(jié)的一些經(jīng)驗(yàn)?zāi)軒椭酱蠹摇?/p>
工作日 8:30-12:00 14:30-18:00
周六及部分節(jié)假日提供值班服務(wù)