序言幾個月前,十分有幸參加微信小程序第一批內(nèi)測. 但那時,遠(yuǎn)沒有現(xiàn)在激動. 因為,那個時候并不曾意 ...
序言
幾個月前,十分有幸參加微信小程序第一批內(nèi)測. 但那時,遠(yuǎn)沒有現(xiàn)在激動. 因為,那個時候并不曾意識到幾個月后, 微信小程序?qū)⑺⒈愕呐笥讶Α?21日深夜,微信官方放了一份關(guān)于”小程序”的內(nèi)測邀請函,某公眾號高調(diào)推送一篇文章,迅速引起朋友圈刷屏,很快便突破了10W+訪問. 甚至有人連夜通宵寫”小程序”開發(fā)教程. 這幾日,內(nèi)測的幾個團隊紛紛給出開發(fā)體驗,于是我也把平日同事比較好奇的幾點問題以及自己的看法和心得總結(jié)出來.
開發(fā)體驗過程中,感覺到微信小程序應(yīng)該算作一種 Hybird App,但并非像phoneGap一樣視圖完全依靠webview來顯現(xiàn),然后通過封裝的 Javascript 與Native API通訊. 小程序的視圖內(nèi), 即可以包括webview,也可以包括native view. 互相覆蓋疊層展示. 相對來說,這種開發(fā)難度更大,但是實現(xiàn)起來比單純的webview更加靈活. 同時對于H5中性能不足的地方,可以用Native實現(xiàn). 對于用戶體驗來說是更接近原生了. 小程序的應(yīng)用框架MINA完成了最難的部分,作為應(yīng)用開發(fā)者的我們,只需要微信提供的wxml,wxss即可完成接近于H5的開發(fā)體驗,完全不必去了解我所用的INPUT是 webview 的 INPUT 還是 Native 的 INPUT。接下來就一步一步帶你走進小程序的世界。
MINA 這個名字就由來就好比MariaDB名字的由來。:0)
這是開發(fā)給到的架構(gòu)圖.
從圖中可以看出,應(yīng)用層和頁面視圖層之間的通訊是經(jīng)由系統(tǒng)層的JSBridge實現(xiàn)的,頁面單向接受來自應(yīng)用邏輯層的數(shù)據(jù)流,應(yīng)用邏輯層響應(yīng)頁面視圖層的事件。頁面視圖層的每一個頁面由wxss和wxml構(gòu)成。而系統(tǒng)層JSBridage提供原生各種能力支持.
官方提供了開發(fā)者工具,雖然開發(fā)者工具和微信客戶端實現(xiàn)的原理大不一樣,但我們依然可以從這里去理解頁面視圖層和應(yīng)用邏輯層.
開發(fā)者工具和我們平時Chrome調(diào)試一樣,左邊是頁面,右邊是控制臺。但不同的是, 左邊展示的頁面和右邊的代碼似乎不是運行在同一個環(huán)境。因為平時調(diào)試時,我們在console輸入document.body.outerHTML會打出頁面HTML代碼。在開發(fā)者工具中只會打印出appservice的頁面內(nèi)容。并非顯示頁面的內(nèi)容。
同時刷新左側(cè)區(qū)域,右邊代碼不會再次執(zhí)行,除非點重啟按鈕。因此懷疑左側(cè)頁面和右側(cè)控制臺并非同一環(huán)境, 可以認(rèn)為左側(cè)頁面就是架構(gòu)圖中的頁面視圖層,右側(cè)控制臺(除了wxml Tab外) 就是邏輯應(yīng)用層。邏輯應(yīng)用層想要更新頁面就必須使用:
this.setData({text: 'update text'});
除此之外,別無它法。妄想通過DOM操作、Zepto、或者其它框架來更新頁面是行不通的,因為在應(yīng)用邏輯層無法直接獲得頁面視圖層中的DOM節(jié)點。甚至在手機客戶端上,完全沒有window、document等全局對象。 開發(fā)者工具只是對整個架構(gòu)的模擬,其中應(yīng)用邏輯層應(yīng)該還是運行在webview之上的,手機客戶端的實現(xiàn)原理應(yīng)該大不一樣、應(yīng)用邏輯層有可能完全不運行在webview上,這也就是為何手機客戶端沒有window、document等對象。
小程序?qū)崿F(xiàn)數(shù)據(jù)綁定的方式如下:
<view>{{msg}}</view>
視圖中以wxml語法添加一個節(jié)點,對于Vue或者是AngularJS用戶是不是感覺頗為親切和熟悉。唯一的區(qū)別就是沒有v-text或者ng-bind的功能。
Page({
data: {
msg: 'Orginal msg'
},
onLoad: function () { this.setData({
msg: 'Updated msg'
});
}
});
更新數(shù)據(jù)時,對比三者的代碼實現(xiàn):
// 微信小程序this.setData({
msg: 'Updated msg'});// Vuethis.msg = 'Updated msg';// AngularJS$scope.msg = 'Updated msg';
接著我們加入另外一個INPUT
<input value="{{msg}}"></input>
對于INPUT,小程序并不存在類似 ng-model 或者是 v-model 的雙向綁定指令,只能通過 value 進行設(shè)置. 當(dāng)用戶人工在INPUT中修改其中的值后,發(fā)現(xiàn) view 中的值并不會跟著變, 這里與 Vue 和 AngularJS 表現(xiàn)不一致.
因為小程序是應(yīng)用邏輯層到頁面視圖層的單向綁定,所以在應(yīng)用邏輯層中不會感知到值的變化,而且也并不提供一個 getData 的方法去取到INPUT中的值,只能通過事件響應(yīng)實現(xiàn)。而 Vue和 AngularJS 的雙向綁定特性,人為在頁面上的數(shù)據(jù)改變是可以直接反饋到邏輯代碼里的。如果一定要實現(xiàn)類似功能,那就只有通過響應(yīng)INPUT事件,實時更新view的數(shù)據(jù). 我們再在 頁面加個按鈕:
<button bindtap="btnTap">Button</button>
邏輯層加入響應(yīng)事件:
btnTap: function () { this.setData({
msg: 'Updated msg'
});
}
手動改變INPUT值為其它時,INPUT的值卻并沒有改回來。這里說明在 setData 操作時,應(yīng)用邏輯層會先檢測msg是否有變化,而此時應(yīng)用邏輯層感知不到到人為修改的INPUT值,因此 setData 操作會被視為沒有改變數(shù)據(jù)而不會去更新視圖。所以官方提供另外一種方法強制更新視圖:
this.setData({
msg: 'Updated msg',
}, {forceUpdate: true});
至于為何在 setData 時要檢測數(shù)據(jù)是否變化過呢,而不是每次 setData 都去直接更新視圖呢?我猜想是在應(yīng)用邏輯層數(shù)據(jù)傳遞到頁面視圖層的這個過程,并非像暜通H5中dom.innerHTML = 'updated';一樣簡單,因此做這個檢測也是起到對性能的一種保護作用吧。
再來看另外一種情況,非當(dāng)前執(zhí)行序列下更新數(shù)據(jù)。 修改btnTap事件如下:
btnTap: function () { var self = this; this.setData({
msg: 'Updated by button tap'
});
setTimeout(function () {
self.setData({
msg: 'Updated by setTimeout'
});
}, 3000);
}
但是結(jié)果并非像預(yù)期的那樣三秒后改變文字。同理,此類情況如果是用AngularJS實現(xiàn)需要修改為:
setTimeout(function () {
$scope.msg = 'Update by setTimeout';
apply();
});
添加一行apply();或者用提供的$timeout方法。 而Vue即便在setTimeout中也可以不作改變,直接賦值。因為它的數(shù)據(jù)綁定是基于getter、setter的。 所以在MINA中也可通過類似方法實現(xiàn):
setTimeout(function () {
self.setData({
msg: 'Updated by setTimeout'
});
self.update();
}, 3000);
所以看到這里,MINA在做數(shù)據(jù)綁定的時候和AngularJS的臟數(shù)據(jù)檢查機制很像呢。
小程序提供了一系列網(wǎng)絡(luò)請求API,支持 HTTP 請求、webSocket請求、以及上傳下載文件。 但前提條件是在后臺配置好了合法域名,只有合法域名的請求才被允許。
因為業(yè)務(wù)只涉及到 HTTP 請求, 所以這里只討論由wx.request發(fā)起的 HTTP 請求。 官方規(guī)定最多只允許5個并發(fā)請求,并且暫時不提供定制的方法。
這個讓我聯(lián)想到了多數(shù)瀏覽器對單個域名最高并發(fā)數(shù)量設(shè)置為6個。與之不同的是:
瀏覽器的策略是針對單域名,但小程序中是針對所有請求(但因為合法域名只有一個,所以也就不存在多域名的問題)。 瀏覽器在單個域名請求超過6個時,只是會暫時阻塞后續(xù)請求,直至某個請求完成,但小程序似乎在多于5個請求并發(fā)時直接報錯。
先忽略這個問題,那么,線上已經(jīng)開發(fā)好了的接口是否可以直接使用呢?那就看以下幾點:
小程序有新的AppId,如果以前接口是針對老的AppId開發(fā)的話,那肯定不適用。 自從 iOS9 推出 ATS 特性后,要求 App 內(nèi)訪問的網(wǎng)絡(luò)必須使用 HTTPS 協(xié)議以保證網(wǎng)絡(luò)鏈路安全,所以小程序也需要接口支持 HTTPS 協(xié)議。 客戶端對于 HTTP 協(xié)議的一些特性不完全支持,比如 cookie。因此如果接口從 cookie讀數(shù)據(jù)的,就需要修改為從參數(shù)讀取。同理寫 cookie 也需要修改為返回在 body 中,然后在邏輯層用 storge API模擬實現(xiàn) cookie。另外還有一些比如返回 Content-type必須為 utf-8,否則客戶端解析亂碼等問題,都需要在接口改造時注意。 出于安全考慮,部分 header 用戶是無法自行定義的,如果接口中存在 Referer 校驗等類似問題的話可能要重新修改校驗規(guī)則。 請求由客戶端發(fā)出,因此為方便跨域的 jsonp 請求就沒有存在的必要。
綜上所述,在請求后端接口上大體還是和以前體驗差不多的。
客戶端請求是由客戶端發(fā)起的比較好理解,因為之前就判斷客戶端的應(yīng)用邏輯層代碼不是運行在webview上。小程序開發(fā)者工具的應(yīng)用邏輯層代碼應(yīng)該是運行在webview上的,那么它的 http 請求是由 node 發(fā)起的還是 webview 發(fā)起的呢? 出于好奇研究了一下,發(fā)現(xiàn) wx.request 在開發(fā)者工具中是由 webview 發(fā)起的 xhr 請求:
既然是 webview 發(fā)起的 xhr 那么肯定就會受到瀏覽器跨域安全策略的限制。
分別在普通瀏覽器和小程序開發(fā)者工具的 console 中注入 jQuery 后執(zhí)行:
$.get('http://www.qq.com');
可以發(fā)現(xiàn)普通瀏覽器會在xhr.send()時報錯,但開發(fā)者工具不會,而且能正常返回結(jié)果。
普通瀏覽器:
小程序開發(fā)者工具:
由此可見,開發(fā)者工具使用了一些手段屏蔽或者繞過了 webview 的跨域安全策略。
以上體驗都是在手機充值小程序的開發(fā)過程中的心得與體會,希望能通過梳理的幾點問題能夠大致了解到小程序的工作方式。因為我們的業(yè)務(wù)比較簡單,使用到的技術(shù)可能只是MINA中的冰山一角,所以本文涉及的內(nèi)容有一定的局限性,后續(xù)新的心得與體會也會在這里補充,更新。
從8月初開發(fā)小程序到現(xiàn)在,起初每天工作超過15個小時,從剛開始一步一個坑,框架改了又改到現(xiàn)在框架基本穩(wěn)定,開公內(nèi)測。這一切都離不開WX GG們的辛苦努力,這幫天才GG們賣得一手好萌,寫得一手好代碼,更重要的是他們精力充沛,無時無刻都在幫我們定位問題,解決問題。付出的努力是值得的,從現(xiàn)在小程序的轟動趨勢來看,小程序注定要創(chuàng)建一個***。
最后,手機充值小程序井然有序的在進行著一步一步的迭代,這個過程更加順暢得心應(yīng)手,感謝手機充值團隊的產(chǎn)品、后臺、視覺、重構(gòu)等同學(xué)付出的努力,希望大家多多支持手機充值,也希望在小程序上線之日,手機充值小程序能以最優(yōu)雅的姿態(tài)與大家見面。
工作日 8:30-12:00 14:30-18:00
周六及部分節(jié)假日提供值班服務(wù)