2017年1月9日,咱們贏來了微信小程序,而在今年,小程序已經在徹底融入到咱們的生活中,能夠說無處不在,贏來了一場真正的大爆發。微信之父張小龍在形容小程序時,是這樣說的php
小程序是一種不須要下載安裝便可使用的應用,它實現了應用「觸手可及」的夢想,用戶掃一掃或者搜一下便可打開應用。也體現了「用完即走」的理念,用戶不用關心是否安裝太多應用的問題。應用將無處不在,隨時可用,但又無需安裝卸載。
而網友們在形容小程序的時候,是這麼說的html
App就像原配,一年用不了幾回;
服務號就像小三,每月固定用幾回;
小程序就像炮友,用完即走;
那麼在你點擊啓動一個小程序的時候,這個「p友」是如何作到無需安裝卸載,觸手可及,用完即走的呢,這其中在交互方面又發生了哪些不可告人的事情,讓她如此特殊,下面以我最近接觸到的一款小程序爲例,簡單總結一下小程序底層框架和一些api接口方面的設計思路。前端
你們都說小程序體驗好,即開即用,比普通Webview的H5頁面體驗好不少,這個問題的我認爲須要從幾個方面考慮,首先呢,拋開業務層面的設計和優化,就是小程序底層框架的設計和實現方面的特色。ios
當咱們新建或打開一個小程序項目,以唱吧比賽小程序爲例,便可看到以下圖的項目結構。git
官方文檔規定,入口文件app.js, 全局樣式文件app.wxss,全局配置文件app.json,
每一個頁面中再分視圖wxml,wxss和邏輯js、文件配置json等,從這裏咱們能夠看出,整個小程序的上層框架,也就是大致分爲視圖層和邏輯層兩個部分。
(官方文檔https://developers.weixin.qq....)github
小程序採用的MINA框架,View層主要用來渲染頁面結構,App Service層用來邏輯處理、數據請求、接口調用,它們在兩個線程裏運行,整個小程序是隻有一個App Service的,而且整個小程序的生命週期內它是常駐內存的。View層主要使用WebView渲染,而App Service邏輯層是使用JSCore運行。ajax
通訊方面,View和AppService是雙線程通訊的,主要經過系統層的JSBridage進行通訊,AppService把數據變化通知到View,表現方法也就是setData方法,觸發View頁面更新,View把觸發的事件通知到AppService進行業務處理。json
這裏要說的是,小程序是沒有DOM結構的,那麼視圖層的渲染是如何作到的呢,就是運行環境中會把pages中的WXML的節點樹結構,轉化爲JS的對象,進行渲染,這也是小程序體驗優於普通分享頁面的一大緣由,省去了不少關於瀏覽器DOM的操做,由JS運行環境之間進行渲染解析。axios
那麼話說回來,此次在搭建唱吧小程序底層的時候,咱們其實作了哪些事情呢。首先,咱們並無進行純Native層的搭建和改造,而是對上述提到的API層的一次的封裝,尤爲是在關於網絡請求的改造和小程序啓動的登陸流程方面,咱們前端團隊嘗試去作一些分層和優化。小程序
首先網絡請求優化方面,微信提供的請求接口基本長這樣:
wx.request({ url: 'test.php', //僅爲示例,並不是真實的接口地址 data: { x: '' , y: '' }, header: { 'content-type': 'application/json' // 默認值 }, success: function(res) { console.log(res.data) } })
是否是感受和以前的某種請求模式很像,沒錯,就是古老的$.ajax,這時候咱們又想起了ajax的回調地獄,若是頁面的請求不少,請求的順序還有限定,瞬間又是各類嵌套,能夠說是要從請求到懵逼了。
因此爲了解決回調地獄和同時優化請求代碼邏輯,咱們在封裝wx.request的同時,咱們在小程序開發中,引入了async/await語法糖,用到了來自facebook的regenerator模塊(詳情請戳:https://github.com/facebook/r...),async、await函數經babel編譯後,再用regenerator-runtime模塊用於提供功能實現,這一方面也得力於小程序支持ES6語法的編譯。
實現過程當中,單獨用一個公共方法封裝,返回wx.request的promise
//wechat.js const request = (url,options) => { return new Promise((resolve, reject) => { wx.request({ url: url, method: options.method, data: Object.assign({}, options.data), header: options.header, success: resolve, fail: reject }) }) }
以後在咱們的上層公共庫中,編寫與請求相關的處理邏輯。
// changba.js const regeneratorRuntime = require('./regenerator-runtime.js') const wechat = require('./wechat') const URI = 'xxx' const requestAPI = async (url,opt) => { const app = getApp() let options = Object.assign({data: {}},opt) if (/^\/api\/(.+)$/.test(url)) { url = URI + url; } if (!options.method) { options.method = 'POST'; } let header = { 'Content-Type': 'application/x-www-form-urlencoded' } options.header = options.header || header ; //除了login方法 其他接口都要加入sessionInfo也就是後端加密過的session_key if (!url.includes('/checkCode')) { options.data['sessionInfo'] = app.globalData.sessionkey; } let isTimeout = false; try { const ree = await wechat.checkSession(); } catch (error) { isTimeout = true; }; try { if (isTimeout) { let aaa = await login(app); } wx.showLoading({ title: '加載中' }); const res = await wechat.request(url,options) if (res && res.statusCode) { if (res.statusCode != 200) { if (wx.hideLoading) { wx.hideLoading() } wx.showModal({ content: wechat.errMsg(res.statusCode).message || '請求失敗,請從新嘗試', title: '提示', showCancel: false }) } else { if (res.data && res.data.code === 1) { if (wx.hideLoading) { wx.hideLoading() } return res.data } else { // xxx 其餘狀況業務邏輯處理 } } } } catch (error) { console.log('請求異常信息:' + error) if (wx.hideLoading) { wx.hideLoading() } wx.showModal({ content: '請求信息異常', title: '', showCancel: false }) } }
上述封裝過程當中,因此除了考慮到請求超時、檢查用戶身份等操做,還能夠加入session過時等相關其餘的業務處理邏輯,這也是本身搭建請求的好處,針對本身的業務需求,進行匹配和改造。
然而在經歷這樣兩層封裝以後,在寫業務邏輯代碼的過程當中,就能夠一目瞭然的發送請求了,達到邏輯清晰且書寫自如,若是習慣了fetch以及axios的朋友應該都會比較喜歡這種方式。
async getdata() { let self = this; let cb_getdata = await app.changba.requestAPI('/api/xxx', { data: { id: self.data.id } }); if (cb_getdata && cb_getdata.code === 1) { // xxx } }
下面說下,啓動小程序後的登陸流程方面,在這一方面,小程序與其餘不一樣的是,沒有固定的登陸啓動頁面,而是徹底後臺交互,固然根據產品定位和需求,也能夠本身作一套登陸系統,可是微信官方給出的文檔基本長這樣:
沒錯,看着很頭大,然而最頭大的不是你循序漸進的實現了這個流程,而是實現了以後,你還會遇到不少意想不到的問題。
基本的流程不用多說,循序漸進便可,就是使用wx.login()能夠得到開發者服務器向微信接口服務器請求得到session_key等數據時所須要的參數code,開發者服務器以code+appid+appsecret換取用戶惟一標識openid和會話密鑰session_key。但每一次調用wx.login()都會更新微信接口服務器上的session_key。
一樣,改造微信api先,
// wechat 登陸封裝 const login = () => { return new Promise((resolve, reject) => { wx.login({ success: resolve, fail: reject }) }) }
然後,在作本身的登陸封裝時,能夠先去請求微信的code,而後用在本身的請求中,獲取並存儲本身的登陸態。
let we_login = await wechat.login() // 微信登陸 let cb_login = await requestAPI('xxxx/checkCode', { data: {code: we_login.code}}) if (cb_login && cb_login.code === 1) { // xxxx 業務邏輯 } catch (error) { wx.showModal({ title: '登陸提示', content: '登陸失敗', showCancel: false }) }
而在完成上面整套業務邏輯過程當中,可能會遇到一些意想不到的坑,這裏面我印象比較深入的有兩個,第一個是關於受權的問題,另外一個就是關於小程序生命週期與頁面生命週期初始化過程當中異步請求回調順序的問題。
先說第一個問題,關於受權框喚起的問題,也就是你常見的下面這個框。
只有用戶受權後,才能夠進一步獲取用戶的信息,這個框在最初是能夠經過wx.getUserinfo()方法直接喚起,而在5月份之後,微信去掉了這個方法的功能,只能經過固定的button open-type去引導用戶受權。
因此在底層邏輯的設計過程當中,就要拋棄以前login以後獲取用戶受權信息的設計思路,而是進行拆分,將login和受權的邏輯分開。
在必需要受權操做的地方例如咱們小程序中須要「參賽」或者「關注」的地方,進行單獨受權的處理,經過使用wx.getSetting獲取用戶的受權狀況
1) 若是用戶已經受權,直接調用wx.getUserInfo獲取用戶最新的信息
2) 用戶未受權,在界面中顯示一個按鈕提示用戶登入,當用戶點擊並受權後就獲取到用戶的最新信息。
這個問題簡單來講,就是小程序啓動有本身的生命週期onLaunch->onShow->onHide,而每一個page的實例化也有本身的生命週期,onLoad->onShow->onReady->onHide->onUnload
然而在開發過程當中,會遇到在App啓動onLaunch的時候,發起登陸請求,並註冊到咱們本身的服務器上以便使用,然而,這個過程當中,
app on launch -> request -> success -> page onload
是沒法判斷success和page onload哪一個先來的,會致使頁面初始化數據失敗的狀況。
解決方案一
就是在request success中處理,使用getCurrentPages方法獲取是否頁面先於success生成,若是生成咱們就強制讓頁面再次渲染。
這顯然是一種hack的方式, 在實際使用過程中,若是登陸邏輯比較複雜,這個方法不是十分便利,page onload在一些特殊狀況也會被調用,這顯然不是咱們想看到的
if (getCurrentPages().length != 0) { getCurrentPages()[getCurrentPages().length - 1].onLoad() }
解決方案二
目前我在開發中使用的是這種方案,
在login的邏輯裏,增長一個回調函數cbLoginCallBack。
Page頁面判斷一下當前app.globalData.sessionKey是否存在,若是沒有(第一次)則定義定義一個app方法(回調函數)
// Login Request if (app.cbLoginCallBack) { typeof app.cbLoginCallBack == 'function' && app.cbLoginCallBack(cb_login.data) } // 邏輯頁面 if (app.globalData.sessionkey) { // init data } else { app.cbLoginCallBack = res => { if (res) { // init data } } }
App頁面在請求success後判斷時候有Page頁面定義的回調方法,若是有就執行該方法。由於回調函數是在Page裏面定義的因此方法做用域this是指向Page頁面。
雜七雜八寫了不少,基本都是近期開發和學習過程當中本身對小程序體驗和交互方面的一點總結,在這裏跟你們分享,以便遇到一樣的問題,能夠一塊兒探討,找出最優的解決方案,今年小程序的熱度還將持續一陣,各大小公司持續發力,將來在這個領域還會有哪些事情發生,讓咱們拭目以待。