本文發表至今已有一段時間,錯別字多、文筆混亂、內容過於陳舊。本人建議讀者沒必要細究,大概瀏覽便可,最新的開發指南仍是以官方文檔爲準,該博文的示例代碼通過了重構,已經與官方文檔同步,可能與文中的代碼片斷有較大差別,請以 Github 倉庫上的代碼爲準。css
最近微信小程序被炒得很火熱,本人也抱着試一試的態度下載了微信web開發者工具,開發工具比較簡潔,功能相對比較少,個性化設置也沒有。瞭解完開發工具以後,順便看了一下小程序的官方開發文檔,大概瞭解了小程序的開發流程和一些經常使用的API。html
瞭解了小程序以後,本身就有了想要作一個小demo的衝動,雖然本身對小程序尚未作過不少實踐,只是在官方例子上徘徊,可是仍是想作出點小東西。既然要作一個demo,天然須要到數據,本身有又不想獨自搭建服務端,因此在網上搜索能夠用來提供測試數據的免費api,最後我選擇了豆瓣圖書。豆瓣圖書提供的api功能比較少,加上不開放appkey申請,因此沒法操做用戶數據。只能作點簡單的圖書查詢和圖書詳細信息展現,這個demo只有兩個頁面,很是之簡單。vue
demo中用到的豆瓣圖書api只有兩個,一個是圖書搜索,另外一個是獲取圖書詳情。react
GET https://api.douban.com/v2/book/search
jquery
參數 | 意義 | 備註 |
---|---|---|
q | 查詢關鍵字 | q和tag必傳其一 |
tag | 查詢的tag | q和tag必傳其一 |
start | 取結果的offset | 默認爲0 |
count | 取結果的條數 | 默認爲20,最大爲100 |
返回status=200git
{ "start": 0, "count": 10, "total": 30, "books" : [Book, ...] }
GET https://api.douban.com/v2/book/:id
github
參數 | 意義 |
---|---|
:id | 圖書id |
如下是具體圖書的詳情信息,部分demo中用不到的信息省略web
{ "id":"1003078", "title":"小王子", "alt":"https:\/\/book.douban.com\/subject\/1003078\/", "image":"https://img3.doubanio.com\/mpic\/s1001902.jpg", "author":[ "(法)聖埃克蘇佩裏" ], "publisher":"中國友誼出版公司", "pubdate":"2000-9-1", "rating":{"max":10,"numRaters":9438,"average":"9.1","min":0}, "author_intro":"聖埃克蘇佩裏(1900-1944)1900年,瑪雅·戴斯特萊姆......", "catalog":"序言:法蘭西玫瑰\n小王子\n聖埃克蘇佩裏年表\n" }
項目取名爲DouBanBookApp,項目的結構小程序默認的結構同樣ajax
DouBanBookApp pages index 首頁 index.js index.wxml index.wxss detail 詳情頁 detail.js detail.wxml detail.wxss requests api.js API地址 request.js 網絡請求 utils util.js 工具 app.js app.json app.wxss
應用的主調色參考了豆瓣app的色調,採用了偏綠色。json
首頁頂部展現搜索輸入框,用戶輸入圖書名稱,點擊搜索按鈕,展現圖書列表。圖書可能會不少,不能一會兒所有展現,須要用到分頁,app上最多見的列表分頁就是上拉加載模式,根據小程序提供的組件中,找到了一個比較符合場景的scroll-view組件,這個組件有一個上拉到底部自動觸發的bindscrolltolower
事件。
先製做出界面的靜態效果,以後再整合API,因爲本人對界面設計不敏感,因此隨便弄了一個粗糙的佈局,看得過去就好了,嘿嘿~~
index.wxml
<view class="search-container"> <input type="text" placeholder="輸入書名搜索"></input><icon type="search" size="20"/> </view> <scroll-view scroll-y="true" style="width:100%;position:relative;top:40px;height:200px"> <view style="text-align:center;padding-top:50rpx;"> <icon type="cancel" color="red" size="40" /> <view><text>沒有找到相關圖書</text></view> </view> <view style="text-align:center;padding-top:50rpx;"> <icon type="search" size="60" /> <view><text>豆瓣圖書</text></view> </view> <view class="header"> <text>圖書 10本圖書</text> </view> <view class="common-list"> <view class="list-item"> <view class="index-list-item"> <view class="cover"> <image class="cover-img" src="images/demo.png"></image> </view> <view class="content"> <view class="title">圖書標圖</view> <text class="desc">9.0/oopsguy/2016-07-08</text> </view> </view> </view> </view> <view class="refresh-footer"> <icon type="waiting" size="30" color="reed" /> </view> </scroll-view>
index.wxss
page { background: #F2F1EE; } /*seach*/ .search-container { position: fixed; top: 0; right: 0; left: 0; background-color: #42BD56; color: #FFF; height: 40px; padding: 0 10rpx; z-index: 100; } .search-container input { background: #FFF; color: #AAA; margin-top: 5px; padding: 5px 10rpx; height: 20px; border-radius: 8rpx; } .search-container icon { position: absolute; top: 10px; right: 20rpx; } /*header*/ .header { padding: 20rpx 30rpx; } .header text { color: #A6A6A6; } /*common list*/ .list-item { position: relative; overflow: hidden } /*index list*/ .index-list-item { background: #FFF; padding: 15rpx 30rpx; overflow: hidden; } .index-list-item::active { background: #EEE; } .index-list-item .cover { float: left; width: 120rpx; height: 160rpx; overflow: hidden } .index-list-item .cover image.cover-img { width: 120rpx; height: 160rpx; } .index-list-item .content { margin-left: 140rpx; } .index-list-item .title { display: inline-block; height: 90rpx; padding-top: 20rpx; overflow: hidden; } .index-list-item .desc { display: block; font-size: 30rpx; padding-top: 10rpx; color: #AAA; white-space:nowrap; overflow: hidden; text-overflow: ellipsis; } .refresh-footer { text-align: center; padding: 10rpx 0; }
圖書詳細頁面就是展現具體的圖書信息,通用首頁穿過了的圖書id來獲取圖書信息以後在展現出來,獲取的過程當中可能有延遲,須要一個加載效果來過渡。
detail.wxml
<view> <view class="cover-container"> <image src="images/demo.png"></image> </view> <view class="book-meta"> <view class="meta-info"> <text class="book-title">圖書標題</text> <text class="other-meta">做者:做者名稱</text> <text class="other-meta">出版社:xxx出版社</text> <text class="other-meta">出版日期:2010-05-07</text> </view> <view class="range"> <text class="score">0</text> <text class="viewers">0</text> </view> </view> <view class="book-intro"> <view class="intro-header"><text>簡介</text></view> <text class="intro-content"> 這是圖書簡介 </text> </view> <view class="book-intro"> <view class="intro-header"><text>做者</text></view> <text class="intro-content"> 這是做者簡介 </text> </view> </view> <loading> 加載中... </loading>
detail.wxss
page { background: #EEE; } .cover-container { background: #42BD56; text-align: center; padding: 50rpx 0; } .cover-container image { display: inline-block; width: 300rpx; height: 400rpx; } .book-meta { position: relative; padding: 20rpx; overflow: hidden; } .book-meta .range { position: absolute; top: 30rpx; right: 20rpx; width: 180rpx; background: #FFF; padding: 20rpx 10rpx; text-align: center; box-shadow: 2px 2px 10px #CCC; } .book-meta .meta-info { margin-right: 200rpx; } .meta-info text { display: block } .book-title { font-weight: bold; font-size: 50rpx; } .other-meta { padding-top: 10rpx; color: #888; font-size: 30rpx; } .range text { display: block; } .range .score { font-size: 50rpx; font-weight: bold; } .range .starts { font-size: 40rpx; } .range .viewers { font-size: 30rpx; } .book-intro { padding: 20rpx; font-size: 40rpx; } .book-intro .intro-header { color: #888 } .book-intro .intro-content { font-size: 35rpx; line-height: 45rpx; }
作好了首頁和詳細頁的靜態頁面,接下來就是經過網絡請求api來獲取數據,並顯示到頁面上來。
爲了更好的管理api,我把api專門放到了一個單獨的api.js文件中
api.js
const API_BASE = "https://api.douban.com/v2/book"; module.exports = { API_BOOK_SEARCH: API_BASE + "/search", API_BOOK_DETAIL: API_BASE + "/:id" }
有些常常用到的工具函數放到了util.js中
util.js
function isFunction( obj ) { return typeof obj === 'function'; } module.exports = { isFunction: isFunction }
微信小程序提供了一個用於網絡請求的api:wx.request(OBJECT)
,具體的參數跟jquery的ajax方法差很少,爲了方便調用,我把網絡請求放到了request.js中
request.js
var api = require('./api.js'); var utils = require('../utils/util.js'); /** * 網路請求 */ function request(url, data, successCb, errorCb, completeCb) { wx.request({ url: url, method: 'GET', data: data, success: function(res) { utils.isFunction(successCb) && successCb(res.data); }, error: function() { utils.isFunction(errorCb) && errorCb(); }, complete: function() { utils.isFunction(completeCb) && completeCb(); } }); } /** * 搜索圖書 */ function requestSearchBook(data, successCb, errorCb, completeCb) { request(api.API_BOOK_SEARCH, data, successCb, errorCb, completeCb); } /** * 獲取圖書詳細信息 */ function requestBookDokDetail(id, data, successCb, errorCb, completeCb) { request(api.API_BOOK_DETAIL.replace(':id', id), data, successCb, errorCb, completeCb); } module.exports = { requestSearchBook: requestSearchBook, requestBookDokDetail: requestBookDokDetail }
首頁有圖書搜索和列表展現,上拉加載的效果。微信小程序中沒有了DOM操做的概念,一切的界面元素的改變都要經過數據變化來改變,因此須要在js中的Page中的data中聲明不少數據成員。
用戶在輸入數據時,輸入框的input
綁定了searchInputEvent
事件,就回捕獲到輸入的數據,把輸入的數據更新的data中的searchKey中。
searchInputEvent: function( e ) { this.setData( { searchKey: e.detail.value }); }
當點擊搜索按鈕是,觸發tap
事件,其綁定了searchClickEvent
searchClickEvent: function( e ) { if( !this.data.searchKey ) return; this.setData( { pageIndex: 0, pageData: [] }); requestData.call( this ); }
requestData
中封裝了請求圖書列表的方法
/** * 請求圖書信息 */ function requestData() { var _this = this; var q = this.data.searchKey; var start = this.data.pageIndex; this.setData( { loadingMore: true, isInit: false }); updateRefreshBall.call( this ); requests.requestSearchBook( { q: q, start: start }, ( data ) => { if( data.total == 0 ) { //沒有記錄 _this.setData( { totalRecord: 0 }); } else { _this.setData( { pageData: _this.data.pageData.concat( data.books ), pageIndex: start + 1, totalRecord: data.total }); } }, () => { _this.setData( { totalRecord: 0 }); }, () => { _this.setData( { loadingMore: false }); }); }
上拉加載的效果是一個小球不停的變換顏色,須要一個顏色列表
//刷新動態球顏色 var iconColor = [ '#353535', '#888888' ];
而後用一個定時器來動態改變小球圖標的顏色
/** * 刷新上拉加載效果變色球 */ function updateRefreshBall() { var cIndex = 0; var _this = this; var timer = setInterval( function() { if( !_this.data[ 'loadingMore' ] ) { clearInterval( timer ); } if( cIndex >= iconColor.length ) cIndex = 0; _this.setData( { footerIconColor: iconColor[ cIndex++ ] }); }, 100 ); }
詳細頁面的顯示須要到首頁點擊了具體圖書的id,因此須要首頁傳值過來,這裏用到了小程序土工的wx.navigateTo
方法,給其指定的url
參數後面帶以查詢字符串格式形式的參數,被跳轉的頁面就會在onLoad
方法中獲得值。
//跳轉到詳細頁面 toDetailPage: function( e ) { var bid = e.currentTarget.dataset.bid; //圖書id [data-bid] wx.navigateTo( { url: '../detail/detail?id=' + bid }); }
detail.js中接受參數
onLoad: function( option ) { this.setData({ id: option.id }); }
其實小程序的頁面製做跟平時的html和css差很少,只是頁面中不能用傳統的html標籤,而是改用了小程序提供的自定義標籤,小程序對css的支持也有限制,注意哪些寫法不兼容也差很少懂了。操做頁面變化是經過數據變化來表現出來的,這點有點像react和vue。以上的demo用到的知識點並很少,主要是頁面的數據綁定、事件綁定、模版知識和網絡請求等相關api。仔細看看文檔也差很少能夠作出一個小例子。
qi
整體來講,Demo很簡單,只有兩個頁面,界面也是醜醜的T_T,算是我入門小程序的第一課吧。