微信小程序最近很火,火到什么程度,只要你一打开微信,就是它的身影,几乎你用的各个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) }) } } })
总体来讲,本身的思路就像是上面的代码所描述的,这个只是初步的构想,还有不少东西须要完善:
这些问题对于刚接触的我来讲,还须要一点时间来消化,暂且就贴这么多吧。