本期想要和大家分享一下大眾點評點餐小程序開發(fā)中的邏輯層的經(jīng)驗。與視圖層微信自己定義了一套與HTML對應的WXML和WXSS不同,小程 ...
本期想要和大家分享一下大眾點評點餐小程序開發(fā)中的邏輯層的經(jīng)驗。
與視圖層微信自己定義了一套與HTML對應的WXML和WXSS不同,小程序的邏輯層還是使用javascript編寫的。不過與我們普通的編寫js還是有一些區(qū)別的。接下來我會根據(jù)實踐進行說明。邏輯層代碼結構為
menu
├── menu.html
├── menu.js
├── menu.json
└── menu.less
app.js
作為邏輯層我們只需要關注app.js和menu.js。
小程序提供了App方法來注冊整個小程序,在App方法里我們可以傳入一個對象,指定小程序的生命周期函數(shù)以及自定義的函數(shù)或者數(shù)據(jù)。注意這個函數(shù)只能被調用一次。
如上所示,App擁有著4個生命周期函數(shù),我們可以在launch的時候進行一些全局信息的獲取,比如用戶信息,門店信息等等,然后存入到全局數(shù)據(jù)中。這里的數(shù)據(jù)可以被每個頁面訪問到。
小程序針對每個頁面提供了Page的函數(shù)。整個邏輯層大部分的代碼都會寫在Page函數(shù)中,Page中承接著整個頁面的數(shù)據(jù),生命周期函數(shù),以及在視圖中綁定的事件的觸發(fā)函數(shù),例如各點擊事件。整個Page函數(shù)允許的參數(shù)如下所示:
如上,Page函數(shù)因為是頁面級別的,所以擁有著更多的生命函數(shù),會有下拉刷新事件,會有頁面到達底部的事件。這里我們需要區(qū)別好各個生命周期函數(shù)。onLoad只會在初始化的時候調用一次,onShow是每次打開頁面都會調用,onReady只有頁面初次渲染完成才會被調用。onHide會在navigateTo(微信提供的跳轉API)或者底部tab切換時調用,onUnload會在redirectTo(微信提供的redirect的API)或者navigateBack(微信提供的回退的API)的時候調用。Page更具體的渲染過程可以參考下面這張圖:
簡單描述下就是:視圖層和邏輯層同時進行初始化的操作:視圖層ready之后通知邏輯層發(fā)送數(shù)據(jù);邏輯層執(zhí)行onload和onShow方法,然后等待視圖層的通知,在接收到視圖層的通知之后發(fā)送數(shù)據(jù)給視圖層,然后繼續(xù)等待視圖層的通知。視圖層根據(jù)數(shù)據(jù)進行初次渲染后通知邏輯層渲染完畢,邏輯層調用onReady方法。然后后續(xù)的行為邏輯層可以通過再次發(fā)送數(shù)據(jù)重新渲染視圖層。
Page的整個工作流程可以參照下面的圖:
小程序內申明的變量和函數(shù)只在該文件內有效,不同的文件可以申明相同名字的變量和函數(shù),并不會相互影響。上面提到App內可以設置全局數(shù)據(jù)。我們在每個Page里面都可以通過全局函數(shù)getApp()來拿到全局的引用實例。然后就可以訪問頁面的數(shù)據(jù)。比如我們在購物車下完單之后回到菜單頁可能會需要進行菜單的刷新,我們在購物車頁面就會調用getApp().data.menuRefresh = true,然后在菜單頁的onShow方法進行判斷,例如:
let app = getApp();
Page(
requestMenu () {
//刷新菜單
};
onShow () {
if (app.data.menuRefresh === true) {
app.data.menuRefresh === false;
this.requestMenu();
}
}
);
在每個Page內,我們還可以用getCurrentPages來獲取當前頁面棧的實例,數(shù)組形式,第一個元素為首頁,最后一個元素為當前頁面。頁面棧的表現(xiàn)情況如下表所示:
路由方式 | 頁面棧表現(xiàn) |
---|---|
初始化 | 新頁面入棧 |
打開新頁面 | 新頁面入棧 |
頁面重定向 | 當前頁面出棧,新頁面入棧 |
頁面返回 | 頁面不斷出棧,直到目標返回頁,新頁面入棧 |
Tab切換 | 頁面全部出棧,只留下新的Tab頁面 |
注意我們不能手動去嘗試修改頁面棧,我們只能根據(jù)頁面棧,來分析是使用哪種微信的API來跳頁面。這里的跳轉API還會在下面進行講解。
小程序是支持模塊化的,支持commonjs的模塊化寫法,也就是module.exports或者exports,這兩個的區(qū)別這里就不細講了,不了解的可以去看下nodejs的module那塊的文檔。小程序目前并不支持引入node_modules,也就是并不支持第三方的模塊,當我們需要使用到外部的依賴的時候,建議直接將代碼拷貝到小程序的目錄中,然后通過相對路徑的require函數(shù)進行引入。
小程序作為微信的一個重要功能,微信的框架提供了非常豐富的微信原生API,可以方便的調起微信提供的能力,除了視圖層的一些原生組件外,還有一些功能性的API,如掃碼,定位,媒體播放,本地存儲以及支付功能等等。
我們這次使用的較多的是通過微信發(fā)起網(wǎng)絡請求以及微信的數(shù)據(jù)存儲。
微信提供了wx.request來發(fā)起請求,注意這個方法發(fā)起的是HTTPS請求。所以在開發(fā)微信小程序之前,大家得先遷一下HTTPS~我們自己在使用API的時候,還用了pinkie這個包將request包裝成了Promise的形式方便我們使用。
比較重要的一點是微信的運行環(huán)境并不是瀏覽器,并不提供cookie的功能。但是用戶我們解決用戶鑒別的問題是帶上用戶的token,用戶的token是在用戶登錄的時候后端生成好了放置到App的全局數(shù)據(jù)中。
我們大眾點評點餐頁面上有大量的菜單數(shù)據(jù),這部分數(shù)據(jù)之前在H5上實現(xiàn)的時候用的是瀏覽器的localstorage。這次切換到微信的storage,代價很小,用了一下適配器模式,將微信的數(shù)據(jù)接口適配成我們需要的接口就好了。這樣也是為了以后的迭代慢慢讓H5與小程序使用同一套代碼。
小程序為了減少用戶使用的時候的困擾,規(guī)定了頁面路徑最多只能有5層,所以我們使用的時候得盡量避免多層級的交互方式。
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參數(shù)的自己定義的函數(shù)
params = query(params);
}
//將跳轉的頁面的參數(shù)保存到全局數(shù)據(jù)中,然后在頁面中可以去拿取,store是自己申明的
app.store(page, params);
wx.navigateBack({
delta: history.length - (index + 1)
});
}
}
由于小程序的框架并非運行在瀏覽器中,所以javascript在web端的一些能力都無法使用,除了上面提到的cookie,還有document,window等等。開發(fā)者所有代碼最終會被打包成一份javascript,在小程序啟動的時候運行,直到小程序銷毀。這一點類似于瀏覽器的ServiceWorker,所以邏輯層也稱之為App Service。
本文時間為2017-03-02,后續(xù)更新或修復請查看官方文檔。