作者介紹:潘逸飛,美團點評工程師,2年Web開發經驗,現在是美團點評點餐團隊的一員。
本文接上期的大眾點評點餐小程式開發經驗 - 檢視層,本期想要和大家分享一下大眾點評點餐小程式開發中的邏輯層的經驗。
與檢視層微信自己定義了一套與HTML對應的WXML和WXSS不同,小程式的邏輯層還是使用javascript編寫的。不過與我們普通的編寫js還是有一些區別的。接下來我會根據實踐進行說明。邏輯層程式碼結構為
menu
├── menu.html
├── menu.js
├── menu.json
└── menu.less
app.js
作為邏輯層我們只需要關注app.js和menu.js。
App和Page
App
小程式提供了App方法來註冊整個小程式,在App方法裡我們可以傳入一個物件,指定小程式的生命週期函式以及自定義的函式或者資料。注意這個函式只能被呼叫一次。
- App
- globalData
- onLaunch
- onShow
- onHide
- onError
- 其他自定義
如上所示,App擁有著4個生命週期函式,我們可以在launch的時候進行一些全域性資訊的獲取,比如使用者資訊,門店資訊等等,然後存入到全域性資料中。這裡的資料可以被每個頁面訪問到。
Page
小程式針對每個頁面提供了Page的函式。整個邏輯層大部分的程式碼都會寫在Page函式中,Page中承接著整個頁面的資料,生命週期函式,以及在檢視中繫結的事件的觸發函式,例如各點選事件。整個Page函式允許的引數如下所示:
- Page
- data
- onload
- onReady
- onShow
- onHide
- onUnload
- onPullDownRefresh
- onReachBottom
- onShareAppMessage
- 其他自定義函式
如上,Page函式因為是頁面級別的,所以擁有著更多的生命函式,會有下拉重新整理事件,會有頁面到達底部的事件。這裡我們需要區別好各個生命週期函式。onLoad只會在初始化的時候呼叫一次,onShow是每次開啟頁面都會呼叫,onReady只有頁面初次渲染完成才會被呼叫。onHide會在navigateTo(微信提供的跳轉API)或者底部tab切換時呼叫,onUnload會在redirectTo(微信提供的redirect的API)或者navigateBack(微信提供的回退的API)的時候呼叫。Page更具體的渲染過程可以參考下面這張圖:
簡單描述下就是:檢視層和邏輯層同時進行初始化的操作:檢視層ready之後通知邏輯層傳送資料;邏輯層執行onload和onShow方法,然後等待檢視層的通知,在接收到檢視層的通知之後傳送資料給檢視層,然後繼續等待檢視層的通知。檢視層根據資料進行初次渲染後通知邏輯層渲染完畢,邏輯層呼叫onReady方法。然後後續的行為邏輯層可以通過再次傳送資料重新渲染檢視層。
Page的整個工作流程可以參照下面的圖:
首先Page的data會被用於頁面的初始化渲染。然後使用者會在頁面上,也就是展示層觸發事件,比如我們點餐的話,點選了加菜按鈕。頁面監聽到這個事件之後,會觸發在Page函式中申明的自定義事件。然後根據具體情況可能會呼叫微信的Api發起請求,根據請求的結果,我們呼叫setData方法,來改變頁面的資料,小程式就會監聽到資料的改變而重新執行渲染的過程。這個寫過React的朋友,應該會很熟悉,React也是在Component裡面申明自定義方法,觸發後通過setState來重新渲染頁面。我們之前的H5就是使用React寫的,所以邏輯層遷移到小程式的代價並不是很大~
getApp和getCurrentPages
小程式內申明的變數和函式只在該檔案內有效,不同的檔案可以申明相同名字的變數和函式,並不會相互影響。上面提到App內可以設定全域性資料。我們在每個Page裡面都可以通過全域性函式getApp()來拿到全域性的引用例項。然後就可以訪問頁面的資料。比如我們在購物車下完單之後回到選單頁可能會需要進行選單的重新整理,我們在購物車頁面就會呼叫getApp().data.menuRefresh = true,然後在選單頁的onShow方法進行判斷,例如:
let app = getApp();
Page(
requestMenu () {
//重新整理選單
};
onShow () {
if (app.data.menuRefresh === true) {
app.data.menuRefresh === false;
this.requestMenu();
}
}
);複製程式碼
在每個Page內,我們還可以用getCurrentPages來獲取當前頁面棧的例項,陣列形式,第一個元素為首頁,最後一個元素為當前頁面。頁面棧的表現情況如下表所示:
路由方式 | 頁面棧表現 |
---|---|
初始化 | 新頁面入棧 |
開啟新頁面 | 新頁面入棧 |
頁面重定向 | 當前頁面出棧,新頁面入棧 |
頁面返回 | 頁面不斷出棧,直到目標返回頁,新頁面入棧 |
Tab切換 | 頁面全部出棧,只留下新的Tab頁面 |
注意我們不能手動去嘗試修改頁面棧,我們只能根據頁面棧,來分析是使用哪種微信的API來跳頁面。這裡的跳轉API還會在下面進行講解。
模組化
小程式是支援模組化的,支援commonjs的模組化寫法,也就是module.exports或者exports,這兩個的區別這裡就不細講了,不瞭解的可以去看下nodejs的module那塊的文件。小程式目前並不支援引入node_modules,也就是並不支援第三方的模組,當我們需要使用到外部的依賴的時候,建議直接將程式碼拷貝到小程式的目錄中,然後通過相對路徑的require函式進行引入。
微信API
小程式作為微信的一個重要功能,微信的框架提供了非常豐富的微信原生API,可以方便的調起微信提供的能力,除了檢視層的一些原生元件外,還有一些功能性的API,如掃碼,定位,媒體播放,本地儲存以及支付功能等等。
我們這次使用的較多的是通過微信發起網路請求以及微信的資料儲存。
發請求
微信提供了wx.request來發起請求,注意這個方法發起的是HTTPS請求。所以在開發微信小程式之前,大家得先遷一下HTTPS~我們自己在使用API的時候,還用了pinkie這個包將request包裝成了Promise的形式方便我們使用。
比較重要的一點是微信的執行環境並不是瀏覽器,並不提供cookie的功能。但是使用者我們解決使用者鑑別的問題是帶上使用者的token,使用者的token是在使用者登入的時候後端生成好了放置到App的全域性資料中。
資料儲存
我們大眾點評點餐頁面上有大量的選單資料,這部分資料之前在H5上實現的時候用的是瀏覽器的localstorage。這次切換到微信的storage,代價很小,用了一下介面卡模式,將微信的資料介面適配成我們需要的介面就好了。這樣也是為了以後的迭代慢慢讓H5與小程式使用同一套程式碼。
導航
小程式為了減少使用者使用的時候的困擾,規定了頁面路徑最多隻能有5層,所以我們使用的時候得儘量避免多層級的互動方式。
為了方便呼叫,我們這次管理頁面跳轉的時候自己封裝了一下函式,就是通過getCurrentPages來對頁面棧進行分析,然後選擇跳轉頁面的方式:
const app = getApp();
module.exports = function go2Page(opts) {
if (!opts) return;
if (!opts.url) return;
let url = opts.url;
//拿到當前的頁面棧
const history = getCurrentPages();
let path = url.split('?')
let params;
if (path.length === 2) {
params = path[1];
}
let page = path[0].split('/').pop();
let index = -1;
for (var i = 0; i < history.length; i++) {
let hPath = history[i].__route__;
let hPage = hPath.split('/').pop();
if (page == hPage) {
index = i;
break;
}
}
if (index === -1) {
//如果不存在這個頁面,直接跳轉
wx.navigateTo({
url: url
});
} else {
//如果存在這個頁面,就回退回去
if (params) {
//query是處理下url引數的自己定義的函式
params = query(params);
}
//將跳轉的頁面的引數儲存到全域性資料中,然後在頁面中可以去拿取,store是自己申明的
app.store(page, params);
wx.navigateBack({
delta: history.length - (index + 1)
});
}
}複製程式碼
Tip
由於小程式的框架並非執行在瀏覽器中,所以javascript在web端的一些能力都無法使用,除了上面提到的cookie,還有document,window等等。開發者所有程式碼最終會被打包成一份javascript,在小程式啟動的時候執行,直到小程式銷燬。這一點類似於瀏覽器的ServiceWorker,所以邏輯層也稱之為App Service。
本文時間為2017-03-02,後續更新或修復請檢視官方文件。
參考資料:
微信小程式開發者文件
本文對你有幫助?歡迎掃碼加入前端學習小組微信群: