現學現賣微信小程式開發(二)
現學現賣微信小程式開發(三):引入Rx,為小程式插上翅膀
很早就註冊了微信小程式內測,因為是拖延症晚期患者,所以一直都沒有學習。直到昨天(2016年12月28日),張小龍童鞋宣佈微信小程式2017年1月9日正式上線,滿屏都是小程式的訊息。我這時才想起來,我不能白交了300塊錢啊,得學啊。
反正學習也得總結,倒不如現學現賣寫一個教程吧。話說回來現學現賣肯定有不少錯誤,大家多包涵哈。廢話少說,我們開工,首先下載微信小程式開發工具 mp.weixin.qq.com/debug/wxado… 選擇自己的平臺下載安裝即可。
註冊小程式號的過程我就不講了(網上這類教程一堆,不用我花時間在這裡。),你要開發的話,最好通過公司申請一個。第一次啟動開發工具時需要掃碼,然後選擇新增一個無appId的專案。做個什麼專案好呢,沒想好,那就還是先把Todo牽出來吧。我們來實現一個微信小程式版的Todo,同樣是帶HTTP後臺的版本。為什麼又選擇Todo呢?因為它的邏輯相對完整,增刪改查都有,HTTP請求、返回俱全。在一個平臺折騰明白這個app就基本可以上手幹活了,這貨簡直就是新時代的Hello World啊。
檔案結構
新建工程時,預設開發工具會生成一個樣例給我們。我們就來看看這個樣例是什麼東東。開發工具的左側是一個預覽,中間那欄是檔案目錄,看目錄結構倒是蠻簡單:
全域性檔案
先來看一下根目錄:這個目錄下有三個檔案app.js,app.json和app.wxss。app.js是應用入口,裡面定義了App() 函式,用來註冊一個小程式,就像Android裡面的Application和Angular2中的。接受一個 object 引數,其指定小程式的生命週期函式等。
//app.js
App({
onLaunch: function () {
//呼叫API從本地快取中獲取資料
var logs = wx.getStorageSync('logs') || []
logs.unshift(Date.now())
wx.setStorageSync('logs', logs)
},
getUserInfo:function(cb){
var that = this
if(this.globalData.userInfo){
typeof cb == "function" && cb(this.globalData.userInfo)
}else{
//呼叫登入介面
wx.login({
success: function () {
wx.getUserInfo({
success: function (res) {
that.globalData.userInfo = res.userInfo
typeof cb == "function" && cb(that.globalData.userInfo)
}
})
}
})
}
},
globalData:{
userInfo:null
}
})複製程式碼
從上面的例子程式碼看的話,雖然簡單還是能看出一些東西的:
- App()本身就是一個函式,在其他頁面可以通過系統提供的getApp()來得到應用的例項。
- onLaunch就是一個生命週期函式,是當小程式初始化完成時,會觸發這個函式(只觸發一次)。這裡在應用初始化時向本地儲存寫入了一個日期。
- globalData是個全域性資料的存放區域,示例程式碼中只放置了一個userInfo作為全域性變數。
- 例子程式碼在這裡定義了一個全域性方法getUserInfo,這個全域性方法的訪問可以通過getApp().getUserInfo()。如果你開啟
pages/index/index.js
,你會發現示例程式碼通過getApp()取得應用例項後呼叫了getUserInfo這個全域性方法
//獲取應用例項
const app = getApp()
...
//呼叫應用例項的方法獲取全域性資料
app.getUserInfo(function(userInfo){
//更新資料
that.setData({
userInfo:userInfo
})
})複製程式碼
那麼類似像onLaunch這樣的生命週期函式有哪些呢?還有onShow,onHide和onError。onShow和onHide分別是應用從後臺切換到前臺以及從前臺進入後臺時呼叫的。當你關閉小程式或者切換微信應用到別的應用時,微信並沒有直接幹掉小程式,小程式只是進入後臺而已,當你切換回來,小程式就又進入了前臺。這個機制其實和Android以及iOS的應用切換很像。onError顧名思義就是小程式發生指令碼錯誤或者api呼叫失敗是觸發的。注意:請不要在其他頁面呼叫生命週期函式
接下來我們看看 app.json
,這個東東呢,就是小程式的應用全域性配置檔案。結構也很簡單:pages定義頁面的檔案路徑,window定義小程式整個視窗的一些元素,比如Title的文字,Title的顏色,視窗背景顏色等等。
{
"pages":[
"pages/index/index",
"pages/logs/logs"
],
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "todos",
"navigationBarTextStyle":"black"
},
"debug": true
}複製程式碼
比如在上面的配置下,視窗的展現形式是這樣的
如果我們改動app.json
成為下面的樣子,那麼是什麼效果呢?
{
"pages":[
"pages/index/index",
"pages/todos/todos"
],
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#000",
"navigationBarTitleText": "todo",
"navigationBarTextStyle":"white",
"backgroundColor": "#492b2b"
},
"debug": true
}複製程式碼
嗯,導航欄背景變成黑色了,文字白色,一切很完美。且慢,我們不是還新增了一個 "backgroundColor": "#492b2b"
嗎,它的效果怎麼沒有出來呢?你點選頭像進入log頁面後,再點選左上角返回會看到一個動畫,這個動畫的背景竟然是我們設定的視窗背景色!?
但為毛在靜態介面上看不到呢,我猜想是由於Page擋在了背景前面,但是不是呢,我們來用試驗驗證一下。怎麼驗證呢,我們在window的設定中再加一個配置 "enablePullDownRefresh": "true"
,這個是啟用下拉重新整理的效果的開關,我們試一下,拖住介面往下拉,果然,視窗背景色在後面藏著呢!
同樣 backgroundTextStyle
定義的是下拉背景字型、loading 圖的樣式(僅支援 dark/light)
在這個配置檔案中,我們還可以定義用於切換介面的工具欄(tab bar),我們也來試驗一下吧。
{
"pages":[
"pages/index/index",
"pages/todos/todos"
],
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#000",
"navigationBarTitleText": "todo",
"navigationBarTextStyle":"white",
"backgroundColor": "#492b2b",
"enablePullDownRefresh": "true"
},
"tabBar": {
"list": [{
"pagePath": "pages/index/index",
"text": "首頁"
}, {
"pagePath": "pages/logs/logs",
"text": "日誌"
}]
},
"debug": true
}複製程式碼
嗯嗯,很醜陋,但是畢竟顯示出來了。那麼問題來了,這個tabBar如何自定義呢?除了list屬性,它還提供color,selectedColor,backgroundColor,borderStyle和postion屬性。我們都設定一下
"tabBar": {
"list": [{
"pagePath": "pages/index/index",
"text": "首頁",
"selectedIconPath": "assets/images/home.png"
}, {
"pagePath": "pages/logs/logs",
"text": "日誌",
"iconPath": "assets/images/log.png"
}],
"color": "#b0bec5",
"backgroundColor": "#2196F3",
"selectedColor": "#fff",
"borderStyle": "#F8BBD0",
"position": "bottom"
}複製程式碼
還是比較醜,碼農加直男,只能湊活了。你可能發現我在list中一個新增的是selectedIconPath,一個新增的是iconPath。其實我是犯懶,不想再找兩個圖片而已。恰好首頁是預設選中的tab,那麼正好兩個圖示都能顯示出來。
除了tabBar,還可以設定一個debug屬性,預設是false,如果設定成true,那麼你在除錯的時候,會發現在console裡面顯示很多log資訊。其資訊有Page的註冊,頁面路由,資料更新,事件觸發等。這個開關在開發時建議開啟,在釋出時記著關閉哈。
再來看 app.wxss
,估計你猜也猜得到,這個就是類似css的樣式表了。這個全域性樣式所有頁面都可以使用。
/**app.wxss**/
.container {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
padding: 200rpx 0;
box-sizing: border-box;
}複製程式碼
像我這麼愛折騰的人,對於這個css肯定要鼓搗一通,看看到底起什麼作用?我們首先加一個背景色,紅色比較顯眼,background-color: red;
。
首頁下方有一片區域沒有著色,先不管它,點頭像進去看看。
這種變化說明這個container選擇器是應用於頁面的,根據頁面內容來決定高度,所以這個100%並不能填滿全屏。好在微信小程式可以接受css的大部分設定,如果把 height: 100%;
改成 height: 100vh;
就可以填滿全屏了。
頁面檔案
頁面檔案也很簡單,基本就是和資料夾同名的 .js
、 .json
、 .wxss
檔案,唯一多了一種型別就是 .wxml
檔案。.js
檔案中定義一個Page函式,用於構建頁面檢視的邏輯,而.wxml
描述頁面檢視的結構和佈局。首先看一下index.js:
var app = getApp()
Page({
data: {
motto: 'Hello World',
userInfo: {}
},
//事件處理函式
bindViewTap: function() {
wx.navigateTo({
url: '../logs/logs'
})
},
onLoad: function () {
console.log('onLoad')
var that = this
//呼叫應用例項的方法獲取全域性資料
app.getUserInfo(function(userInfo){
//更新資料
that.setData({
userInfo:userInfo
})
})
}
})複製程式碼
由上面的結構可以看出基本上Page函式是一個這樣的形式。它接受一個物件,這個物件有一些生命週期函式、事件處理函式和資料物件構成。也就是說上面的程式碼可以改寫成下面這樣的形式。
let app = getApp()
let pageParams = {
data: {
motto: 'Hello World',
userInfo: {}
},
//事件處理函式
bindViewTap: function() {
wx.navigateTo({
url: '../logs/logs'
})
},
onLoad: function () {
console.log('onLoad')
var that = this
//呼叫應用例項的方法獲取全域性資料
app.getUserInfo(function(userInfo){
//更新資料
that.setData({
userInfo:userInfo
})
})
}
}
Page(pageParams)複製程式碼
事實上我們更傾向於這麼寫,因為這樣的寫法可以使我們避免太多巢狀,以及對pageParams做更多靈活的操作。因為是個物件,我們可以寫成 pageParams.data = blablabla
或 pageParams.onLoad = someFunctionCall
。對了,這個物件我也不知道叫什麼,自己給它起的名字而已,反正Page函式是需要這麼一個引數的。
那麼頁面的內建函式有哪些呢?可以參照下表,其中前5項屬於生命週期函式。
內建函式 | 作用 | 呼叫機制 |
---|---|---|
onLoad | 監聽頁面載入 | 生命週期內呼叫一次 |
onReady | 監聽頁面初次渲染完成 | 生命週期內呼叫一次 |
onShow | 監聽頁面顯示 | 每次開啟頁面都會呼叫一次 |
onHide | 監聽頁面隱藏 | 當navigateTo或底部tab切換時呼叫 |
onUnload | 監聽頁面解除安裝 | 當redirectTo或navigateBack的時候呼叫 |
onPullDownRefresh | 監聽使用者下拉動作 | 使用者動作觸發 |
onReachBottom | 頁面上拉觸底事件的處理函式 | 事件觸發 |
onShareAppMessage | 使用者點選右上角分享 | 使用者點選觸發 |
具體可以參看微信官方文件給出的頁面生命週期示意圖
再來看看 index.wxml
,其實就是類似html的一個模版檔案,但是微信自己重造輪子的目的不是很清楚。這裡 class="container"
應用了我們的全域性樣式。
<!--index.wxml-->
<view class="container">
<view bindtap="bindViewTap" class="userinfo">
<image class="userinfo-avatar" src="{{userInfo.avatarUrl}}" background-size="cover"></image>
<text class="userinfo-nickname">{{userInfo.nickName}}</text>
</view>
<view class="usermotto">
<text class="user-motto">{{motto}}</text>
</view>
</view>複製程式碼
事件繫結是通過在元件標籤內加 bindXXX=handler
來完成的,這個 handler
需要在pageParams物件中定義。 {{userInfo.nickName}}
是引用了我們在 index.js
中定義的資料物件的屬性。
index.json
也是蠻簡單,就是設定頁面的一些屬性,比如我們改寫一下的話:
{
"navigationBarTitleText": "首頁"
}複製程式碼
導航欄文字就會變成我們設定的值。
好的,到現在我們應該清楚了大概的微信小應用的基礎知識。下一節我們會嘗試著做我們的Todo應用了。