小程序模板網(wǎng)

微信小程序初探【類微信UI聊天簡單實(shí)現(xiàn)】

發(fā)布時(shí)間:2018-05-08 11:00 所屬欄目:小程序開發(fā)教程

微信小程序最近很火,火到什么程度,只要你一打開微信,就是它的身影,幾乎你用的各個(gè)APP都可以在微信中找到它的復(fù)制版,另外官方自帶的跳一跳更是將它推到了空前至高的位置。對(duì)比公眾號(hào),就我的感覺來說,有以下區(qū)別:

  • 公眾號(hào)略顯繁瑣:我首先要關(guān)注才能看到內(nèi)容,而小程序不用(個(gè)人對(duì)微信公眾號(hào)研究不深,不對(duì)之處還望見諒)
  • 小程序性能要好一些:雖然我不是很清楚小程序用什么實(shí)現(xiàn),就體驗(yàn)來說確實(shí)更接近原生一點(diǎn);但是微信公眾號(hào)是用網(wǎng)頁的形式來展示內(nèi)容的,其中的兼容性和性能問題不用我說,各位luer就已經(jīng)清楚了吧
  • 小程序更易開發(fā):小程序發(fā)布了一套新的代碼規(guī)則,也提供了一系列的組件,對(duì)比公眾號(hào)百家爭鳴的形式確實(shí)要統(tǒng)一得多

廢話說了這么多,我也是最近才開始看小程序的實(shí)現(xiàn)方式,體驗(yàn)了一把,確實(shí)比較爽,以下就是個(gè)人開發(fā)總結(jié):

簡易的官網(wǎng)小程序

微信小程序官網(wǎng)中有個(gè)簡單的小demo,地址在這里:https://mp.weixin.qq.com/debug/wxadoc/dev/index.html,按照它的步驟來,一定是可以運(yùn)行一個(gè)和官方一樣的例子出來的,這里就不貼過程了。主要說一下個(gè)人整體感受:

  • js還是原來的js,css還是原來的css,html方面來說,是改了一點(diǎn)東西,比如:div變成了view,文本變成了text,以及img變成了image,但是換湯不換藥,該怎么用還是怎么用,而且語義也更加明確。
  • 增加了配置文件.json,全局有一個(gè)app.json,是全局的配置,比如導(dǎo)航欄、TAB的配置,全局路由的配置等等,而在每個(gè)頁面中,依然是可以進(jìn)行全局覆蓋的,比如list.json中單獨(dú)規(guī)定了列表頁面長啥樣子。
  • 每個(gè)頁面都具有生命周期(包括啟動(dòng)頁),類似于react/vue的聲明周期,更加明確在哪個(gè)階段可以做哪些事情
  • 代碼組件化,很多封裝的組件都可以簡單引用,比如map,而在微信公眾號(hào)上開發(fā)的時(shí)候,你可能還需要專門寫一個(gè)地圖插件
  • API更加好用,雖然我沒多少開發(fā)過公眾號(hào),但是就之前配置的jssdk來說,就感覺比小程序復(fù)雜,小程序只需要一個(gè)appId就可以了,然后在代碼中直接使用wx對(duì)象來調(diào)用各種API

開發(fā)一個(gè)類似微信UI的簡單聊天程序

只是感興趣稍微做了一下案例,其中功能可能根本就還只是九牛一毛,但是覺得有必要記錄一下,說說自己遇到的問題以及解決辦法,界面整體如下:

 


首先,在app.json中編寫頁面路由,如下:
{
"pages":[ 
"pages/index/index",
"pages/list/list",
"pages/chat/chat"
],
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#000",
"navigationBarTitleText": "WeChat",
"navigationBarTextStyle":"#fff"
}
} 

這里有3個(gè)頁面,首頁放一個(gè)按鈕作為入口,列表頁表示聊天記錄,還有一個(gè)聊天頁。

列表頁沒有什么可以講的,設(shè)置列表頁的標(biāo)題可以在list.json中設(shè)置即可,如下:


// list.json
{
"navigationBarTitleText": "聊天列表"
}
列表頁模擬了一些數(shù)據(jù),然后再點(diǎn)擊每一條的時(shí)候,進(jìn)入單個(gè)聊天頁面當(dāng)中,其中需要將當(dāng)前點(diǎn)擊的一些信息傳入下一個(gè)頁面當(dāng)中,這里僅僅只有名字。
//chat.js
//獲取應(yīng)用實(shí)例
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
})
}
})

 

然后進(jìn)入聊天頁面,首先進(jìn)入聊天頁面我想到的是,每一個(gè)氣泡加上它的頭像是否可以做成一個(gè)組件,因?yàn)橹挥凶笥业膮^(qū)分而已,另外如果再加上時(shí)間的話,再將時(shí)間傳遞過去就可以了。

因此chat.wxml最開始就是這樣規(guī)劃的:


<block wx:for="{{ messages }}" wx:key="messages{{ index }}" >
<template id="{{ item.id }}" is="bubble" data="{{ ...item }}" />
</block>

template中的代碼就不展示了,最開始我寫模板的時(shí)候,是開了一個(gè)codePen,然后模擬寫出來之后,再往模板中套,保證基本的樣子差不多,然后再在模板上進(jìn)行細(xì)微的改動(dòng)就可以了。

聊天頁頂部的標(biāo)題是通過列表頁中傳過來的,在頁面加載完成的時(shí)候,設(shè)置就好了:


// chat.js
// 設(shè)置昵稱
setNickName(option) {
const nickname = option.nickname || 'Marry';
   wx.setNavigationBarTitle({
     title: nickname
});
},

 

最開始的樣子就是這樣子的:

 

至此,基本的頁面形態(tài)就已經(jīng)完成了。

遇到的一些問題:

每次進(jìn)入頁面的時(shí)候,即使聊天內(nèi)容已經(jīng)超過了聊天區(qū)域,都會(huì)顯示為最開始的地方
輸入新的聊天記錄的時(shí)候,如果聊天內(nèi)容不是處于最底部,那么新加的內(nèi)容會(huì)看不到
針對(duì)這兩個(gè)問題,我按照自己最初的想法是:進(jìn)入頁面獲取scrollHieght然后計(jì)算scrollTop值,將其滾動(dòng)就好了,至于第二個(gè)問題按照類似的方法就可以解決了,但是我查看小程序的API之后,并沒有發(fā)現(xiàn)如何計(jì)算scrollHeight的方法。只有類似的API,如:boundingClientRect和scrollTop

好在天無絕人之路,看到了scroll-view中的scroll-into-view屬性,于是就想出了解決上面兩個(gè)問題的方法:

進(jìn)入頁面,獲取歷史紀(jì)錄,獲取最后一條消息的ID值,記為lastId,在渲染的時(shí)候,消息列表中的每個(gè)ID值傳入組件,作為每個(gè)消息記錄的唯一標(biāo)識(shí),然后使用scroll-in-view={{ id }}就可以輕松地使最后一條消息進(jìn)入視野當(dāng)中
在聊天的時(shí)候,新加的記錄會(huì)更新這個(gè)lastId值,這樣就自動(dòng)更新視圖了


// 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: '', // 當(dāng)前輸入
   lastId: '' // 最后一條消息的ID
// ...
},
// ...
 send() {
// ...
const data = {
     id: `msg${++nums}`,
     message: msg,
     messageType: 0,
     url: '../../images/5.png'
};
this.setData({ msg: '', lastId: data.id });
}
});

這樣就可以大致實(shí)現(xiàn)類似于聊天的效果了,但是還有一個(gè)小問題,每次從列表中進(jìn)入單個(gè)聊天頁面的時(shí)候,會(huì)有一個(gè)斜向左上方滑動(dòng)的過程,原因是:頁面的轉(zhuǎn)場動(dòng)畫是向左的,但是自動(dòng)滾動(dòng)到最后一條記錄的動(dòng)作是向上的,所以會(huì)有動(dòng)作疊加,既然這樣,我只需要讓滾動(dòng)的過程延遲一段時(shí)間就好.


// 延遲頁面向頂部滑動(dòng)
 delayPageScroll() {
const messages = this.data.messages;
const length = messages.length;
const lastId = messages[length - 1].id;
   setTimeout(() => {
this.setData({ lastId });
}, 300);
},

 

至此問題就算是解決了,在真機(jī)模擬的時(shí)候,IOS還有一個(gè)問題,就是當(dāng)點(diǎn)擊輸入框的時(shí)候,整體頁面會(huì)向上頂起來,這個(gè)問題我在論壇中也有看到,但是沒有找到解決辦法,如果各位有遇到,還望不吝賜教。

擴(kuò)展延伸
如果是一個(gè)真正的聊天程序應(yīng)該怎么做呢?我的設(shè)想是這樣的:

由于當(dāng)時(shí)自己的機(jī)器由于莫名的原因不能夠進(jìn)行登錄,后來采用了本地開了一個(gè)websocket的服務(wù)器來實(shí)現(xiàn)消息的發(fā)送。服務(wù)器代碼相當(dāng)簡單,只是消息的轉(zhuǎn)發(fā)而已

 


// 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中需模擬歷史消息的發(fā)送以及新加消息的發(fā)送,因此代碼整體看起來是這樣的:


//chat.js
//獲取應(yīng)用實(shí)例
const app = getApp()
const msgs = require('./chat-mock-data.js');

Page({
 data: {
   messages: [], // 聊天記錄
   msg: '', // 當(dāng)前輸入
   scrollTop: 0, // 頁面的滾動(dòng)值
   socketOpen: false, // websocket是否打開
   lastId: '', // 最后一條消息的ID
   isFirstSend: true // 是否第一次發(fā)送消息(區(qū)分歷史和新加)
},
 onLoad(option) {
// 設(shè)置標(biāo)題
this.setNickName(option);
},
//事件處理函數(shù)
 onReady() {
// 連接websocket服務(wù)器
this.connect();
},
 onUnload() {
const socketOpen = this.data.socketOpen;
if (socketOpen) {
     wx.closeSocket({});
     wx.onSocketClose(res => {
       console.log('WebSocket 已關(guān)閉!')
});
}
},
 connect() {
   wx.connectSocket({
     url: 'ws://localhost:12112'
});
   wx.onSocketOpen(res => {
this.setData({ socketOpen: true });
// 模擬歷史消息的發(fā)送
     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 });
// 延遲頁面向頂部滑動(dòng)
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連接打開失敗,請(qǐng)檢查!')
})
},
// 設(shè)置昵稱
 setNickName(option) {
const nickname = option.nickname || 'Marry';
   wx.setNavigationBarTitle({
     title: nickname
});
},
// 延遲頁面向頂部滑動(dòng)
 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 });
},
// 發(fā)送消息
 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)
})
}
}
})

 

整體來說,自己的思路就像是上面的代碼所描述的,這個(gè)只是初步的構(gòu)想,還有很多東西需要完善:

頭像
列表頁和聊天頁新消息的處理
數(shù)據(jù)庫的歷史消息存儲(chǔ)
圖片以及語音的發(fā)送
消息本地化存儲(chǔ)
這些問題對(duì)于剛接觸的我來說,還需要一點(diǎn)時(shí)間來消化,暫且就貼這么多吧。

我只是一只小菜鳥,但我并沒有停下學(xué)習(xí)的腳步^_^ 另外,覺得這篇文章不錯(cuò),可以隨手點(diǎn)個(gè)贊么?求星星



易優(yōu)小程序(企業(yè)版)+靈活api+前后代碼開源 碼云倉庫:starfork
本文地址:http://m.u-renovate.com/wxmini/doc/course/24315.html 復(fù)制鏈接 如需定制請(qǐng)聯(lián)系易優(yōu)客服咨詢:800182392 點(diǎn)擊咨詢
QQ在線咨詢
AI智能客服 ×