微信小程序最近很火,火到什麼程度,只要你一打開微信,就是它的身影,幾乎你用的各個APP均可以在微信中找到它的複製版,另外官方自帶的跳一跳更是將它推到了空前至高的位置。對比公衆號,就個人感受來講,有如下區別:javascript
廢話說了這麼多,我也是最近纔開始看小程序的實現方式,體驗了一把,確實比較爽,如下就是我的開發總結:css
微信小程序官網中有個簡單的小demo,地址在這裏:https://mp.weixin.qq.com/debug/wxadoc/dev/index.html,按照它的步驟來,必定是能夠運行一個和官方同樣的例子出來的,這裏就不貼過程了。主要說一下我的總體感覺:html
.json
,全局有一個app.json
,是全局的配置,好比導航欄、TAB的配置,全局路由的配置等等,而在每一個頁面中,依然是能夠進行全局覆蓋的,好比list.json
中單獨規定了列表頁面長啥樣子。react/vue
的聲明週期,更加明確在哪一個階段能夠作哪些事情map
,而在微信公衆號上開發的時候,你可能還須要專門寫一個地圖插件wx
對象來調用各類API只是感興趣稍微作了一下案例,其中功能可能根本就還只是九牛一毛,可是以爲有必要記錄一下,說說本身遇到的問題以及解決辦法,界面總體以下:vue
代碼倉庫:githubjava
首先,在app.json
中編寫頁面路由,以下:react
{ "pages":[ "pages/index/index", "pages/list/list", "pages/chat/chat" ], "window":{ "backgroundTextStyle":"light", "navigationBarBackgroundColor": "#000", "navigationBarTitleText": "WeChat", "navigationBarTextStyle":"#fff" } }
這裏有3個頁面,首頁放一個按鈕做爲入口,列表頁表示聊天記錄,還有一個聊天頁。git
列表頁沒有什麼能夠講的,設置列表頁的標題能夠在list.json
中設置便可,以下:github
// list.json { "navigationBarTitleText": "聊天列表" }
列表頁模擬了一些數據,而後再點擊每一條的時候,進入單個聊天頁面當中,其中須要將當前點擊的一些信息傳入下一個頁面當中,這裏僅僅只有名字。web
//chat.js //獲取應用實例 const app = getApp() const friends = require('./list-mock-data.js') Page({ data: { friends: friends.list }, gotoChat(event) { const currentUser = event.currentTarget.dataset.user; wx.navigateTo({ url: '../chat/chat?nickname=' + currentUser.nickname }) } })
而後進入聊天頁面,首先進入聊天頁面我想到的是,每個氣泡加上它的頭像是否能夠作成一個組件,由於只有左右的區分而已,另外若是再加上時間的話,再將時間傳遞過去就能夠了。數據庫
所以chat.wxml
最開始就是這樣規劃的:
<block wx:for="{{ messages }}" wx:key="messages{{ index }}" > <template id="{{ item.id }}" is="bubble" data="{{ ...item }}" /> </block>
template
中的代碼就不展現了,最開始我寫模板的時候,是開了一個codePen
,而後模擬寫出來以後,再往模板中套,保證基本的樣子差很少,而後再在模板上進行細微的改動就能夠了。
聊天頁頂部的標題是經過列表頁中傳過來的,在頁面加載完成的時候,設置就行了:
// chat.js // 設置暱稱 setNickName(option) { const nickname = option.nickname || 'Marry'; wx.setNavigationBarTitle({ title: nickname }); },
最開始的樣子就是這樣子的:
至此,基本的頁面形態就已經完成了。
遇到的一些問題:
針對這兩個問題,我按照本身最初的想法是:進入頁面獲取scrollHieght
而後計算scrollTop
值,將其滾動就行了,至於第二個問題按照相似的方法就能夠解決了,可是我查看小程序的API以後,並無發現如何計算scrollHeight
的方法。只有相似的API,如:boundingClientRect
和scrollTop
好在天無絕人之路,看到了scroll-view
中的scroll-into-view
屬性,因而就想出瞭解決上面兩個問題的方法:
ID
值,記爲lastId
,在渲染的時候,消息列表中的每一個ID值傳入組件,做爲每一個消息記錄的惟一標識,而後使用scroll-in-view={{ id }}
就能夠輕鬆地使最後一條消息進入視野當中lastId
值,這樣就自動更新視圖了// chat.wxml <scroll-view scroll-y scroll-with-animation class="chat-content" scroll-top="{{ scrollTop }}" scroll-into-view="{{ lastId }}"> <block wx:for="{{ messages }}" wx:key="messages{{ index }}" > <template id="msg{{ index }}" is="bubble" data="{{ ...item }}" /> </block> </scroll-view> // chat.js Page({ data: { messages: [], // 聊天記錄 msg: '', // 當前輸入 lastId: '' // 最後一條消息的ID // ... }, // ... send() { // ... const data = { id: `msg${++nums}`, message: msg, messageType: 0, url: '../../images/5.png' }; this.setData({ msg: '', lastId: data.id }); } });
這樣就能夠大體實現相似於聊天的效果了,可是還有一個小問題,每次從列表中進入單個聊天頁面的時候,會有一個斜向左上方滑動的過程,緣由是:頁面的轉場動畫是向左的,可是自動滾動到最後一條記錄的動做是向上的,因此會有動做疊加,既然這樣,我只須要讓滾動的過程延遲一段時間就好
// 延遲頁面向頂部滑動 delayPageScroll() { const messages = this.data.messages; const length = messages.length; const lastId = messages[length - 1].id; setTimeout(() => { this.setData({ lastId }); }, 300); },
至此問題就算是解決了,在真機模擬的時候,IOS還有一個問題,就是當點擊輸入框的時候,總體頁面會向上頂起來,這個問題我在論壇中也有看到,可是沒有找到解決辦法,若是各位有遇到,還望不吝賜教。
若是是一個真正的聊天程序應該怎麼作呢?個人設想是這樣的:
因爲當時本身的機器因爲莫名的緣由不可以進行登陸,後來採用了本地開了一個websocket
的服務器來實現消息的發送。服務器代碼至關簡單,只是消息的轉發而已
// server.js const WebSocket = require('ws'); const wss = new WebSocket.Server({ port: 12112 }); wss.on('connection', ws => { console.log('connection established'); ws.on('message', message => { console.log("on message coming"); ws.send(message); }); });
在chat.js
中需模擬歷史消息的發送以及新加消息的發送,所以代碼總體看起來是這樣的:
//chat.js //獲取應用實例 const app = getApp() const msgs = require('./chat-mock-data.js'); Page({ data: { messages: [], // 聊天記錄 msg: '', // 當前輸入 scrollTop: 0, // 頁面的滾動值 socketOpen: false, // websocket是否打開 lastId: '', // 最後一條消息的ID isFirstSend: true // 是否第一次發送消息(區分歷史和新加) }, onLoad(option) { // 設置標題 this.setNickName(option); }, //事件處理函數 onReady() { // 鏈接websocket服務器 this.connect(); }, onUnload() { const socketOpen = this.data.socketOpen; if (socketOpen) { wx.closeSocket({}); wx.onSocketClose(res => { console.log('WebSocket 已關閉!') }); } }, connect() { wx.connectSocket({ url: 'ws://localhost:12112' }); wx.onSocketOpen(res => { this.setData({ socketOpen: true }); // 模擬歷史消息的發送 wx.sendSocketMessage({ data: JSON.stringify(msgs), }) }); wx.onSocketMessage(res => { const isFirstSend = this.data.isFirstSend; const data = JSON.parse(res.data); let messages = this.data.messages; let lastId = ''; // 第一次爲接收歷史消息, // 以後的爲新加的消息 if (isFirstSend) { messages = messages.concat(data); lastId = messages[0].id; this.setData({ messages, lastId, isFirstSend: false }); // 延遲頁面向頂部滑動 this.delayPageScroll(); } else { messages.push(data); const length = messages.length; lastId = messages[length - 1].id; this.setData({ messages, lastId }); } }); wx.onSocketError(res => { console.log(res); console.log('WebSocket鏈接打開失敗,請檢查!') }) }, // 設置暱稱 setNickName(option) { const nickname = option.nickname || 'Marry'; wx.setNavigationBarTitle({ title: nickname }); }, // 延遲頁面向頂部滑動 delayPageScroll() { const messages = this.data.messages; const length = messages.length; const lastId = messages[length - 1].id; setTimeout(() => { this.setData({ lastId }); }, 300); }, // 輸入 onInput(event) { const value = event.detail.value; this.setData({ msg: value }); }, // 聚焦 onFocus() { this.setData({ scrollTop: 9999999 }); }, // 發送消息 send() { const socketOpen = this.data.socketOpen; let messages = this.data.messages; let nums = messages.length; let msg = this.data.msg; if (msg === '') { return false; } const data = { id: `msg${++nums}`, message: msg, messageType: 0, url: '../../images/5.png' }; this.setData({ msg: '' }); if (socketOpen) { wx.sendSocketMessage({ data: JSON.stringify(data) }) } } })
總體來講,本身的思路就像是上面的代碼所描述的,這個只是初步的構想,還有不少東西須要完善:
這些問題對於剛接觸的我來講,還須要一點時間來消化,暫且就貼這麼多吧。