- 分享小程序在app.js中全局管理websocket方案
社区有网友提问怎么在路由切换时保持websocket连接不中断?,我在回答中分享了我在实际项目中使用websocket的方案,这边整理一下。 主要思路是在app.js中全局处理websocket的连接和接收消息,收到消息后再把消息转发到页面,实际用到消息的页面接收消息做后续处理。具体代码如下 要引入mitt.js,百度一下,一个很小的文件(具体代码在文章最后) app.js [代码]const mitt = require('./utils/mitt').mitt ... App({ ... onLaunch: function () { let that = this that.globalData.bus = mitt() ... //连接socket ... //收到消息的回调中 if (msg.length > 0) { that.globalData.bus.emit('_socketMsg', msg) } ... } ... }) [代码] 要用到消息的页面 [代码]const app = getApp() ... Page({ ... socketMsg: function(msg){ //实际处理收到的消息 }, onShow: function () { let that = this app.globalData.bus.on('_socketMsg', that.socketMsg) ... }, onHide: function () { let that = this app.globalData.bus.off('_socketMsg', that.socketMsg) ... }, ... }) [代码] 附:mitt.js [代码]function mitt(all) { all = all || Object.create(null); return { on(type, handler) { (all[type] || (all[type] = [])).push(handler); }, off(type, handler) { if (all[type]) { all[type].splice(all[type].indexOf(handler) >>> 0, 1); } }, emit(type, evt) { (all[type] || []).slice().map((handler) => { handler(evt); }); (all['*'] || []).slice().map((handler) => { handler(type, evt); }); } }; } module.exports = { mitt: mitt } [代码]
2021-07-08 - 小程序简单两栏瀑布流效果
瀑布流又称瀑布流式布局,是比较流行的一种网站页面布局方式。视觉表现为参差不齐的多栏布局,即多行等宽元素排列,后面的元素依次添加到其后,等宽不等高,根据图片原比例缩放直至宽度达到我们的要求,依次放入到高度最低的那一栏。 先上代码:https://developers.weixin.qq.com/s/Fgm5s1mz7Wdm 所谓简单,是指只考虑图片,图片之外的其他元素高度固定,不在考虑范围内。 说一下基本的实现思路: 1、加载列表数据 2、在一个隐藏的view中加载图片,通过image组件的bindload获取图片的实际宽高并存储 3、等所有图片加载完成后遍历列表,将图片插入到高度低的那一栏,同时更新该栏高度 我也考虑过在第二步bindload获取到宽高后就直接插入到栏位中,但是会出现小的图片先加载完先出现到页面中,虽然瀑布流不是普通的列表那样的排序,但是也不能小的图片在上面这样太乱顺序,所以就改成了获取宽高先存储,等所有图片加载完成后再往页面上渲染。 来看看实际的代码 不需要渲染到wxml中的数据,我放到了jsData中,主要是两栏的高度和是否在加载数据的标记。 tempPics是第一次加载的数据,临时存放,用于加载图片宽高 columns是两个栏位的实际展示数据 [代码]jsData: { columnsHeight: [0, 0], isLoading: false }, data: { columns: [ [], [] ], tempPics: [] } [代码] 1、加载列表数据 这一步没什么好说的,主要是触发方式,我的代码里是放在页面加载以及拉到页面底部时触发 [代码]onLoad: function() { this.loadData() }, onReachBottom: function() { this.loadData() } [代码] 加载后将列表数据存到tempPics中,用于页面加载获取宽高 2、在一个隐藏的view中加载图片,通过image组件的bindload获取图片的实际宽高并存储 [代码]<view class="hide"> <image wx:for="{{tempPics}}" src="{{item.pic}}" bindload="loadPic" binderror="loadPicError" data-index="{{index}}" /> </view> [代码] 主要是image组件的bindload来获取实际宽高,这里还增加了binderror,防止出现图片加载出错的时候卡死 [代码]loadPic: function(e) { var that = this, data = that.data, tempPics = data.tempPics, index = e.currentTarget.dataset.index if (tempPics[index]) { //以750为宽度算出相对应的高度 tempPics[index].height = e.detail.height * 750 / e.detail.width tempPics[index].isLoad = true } that.setData({ tempPics: tempPics }, function() { that.finLoadPic() }) } [代码] 获取到宽高后,以750为宽度计算出相对应的高度并存储,然后增加一个加载完成的标记。加载出错后就强制高度为750,这样展示的时候就是一个正方形。 单个图片加载完成并存储后调用finLoadPic方法来判断所有图片是否都加载完成。 遍历列表,只要有一个图片没有加载完成的标记,就判断为没有加载完成。 加载完成后进入下一步。 [代码]finLoadPic: function() { var that = this, data = that.data, tempPics = data.tempPics, length = tempPics.length, fin = true for (var i = 0; i < length; i++) { if (!tempPics[i].isLoad) { fin = false break } } if (fin) { wx.hideLoading() if (that.jsData.isLoading) { that.jsData.isLoading = false that.renderPage() } } } [代码] 3、等所有图片加载完成后遍历列表,将图片插入到高度低的那一栏,同时更新该栏高度 这里需要再便利一遍列表,根据当前栏位的高度情况,将图片插入到高度底的那一栏,同时把这一栏高度加上当前图片的高度(不是实际高度,是上一步以750为宽度算出来的高度) [代码]renderPage: function() { var that = this, data = that.data, columns = data.columns, tempPics = data.tempPics, length = tempPics.length, columnsHeight = that.jsData.columnsHeight, index = 0 for (var i = 0; i < length; i++) { index = columnsHeight[1] < columnsHeight[0] ? 1 : 0 columns[index].push(tempPics[i]) columnsHeight[index] += tempPics[i].height } that.setData({ columns: columns, tempPics: [] }) that.jsData.columnsHeight = columnsHeight } [代码] 在wxml中展示的时候image组件的mode要使用widthFix,同时wxss中图片的高度和宽度一样,这样加载出错的图片可以正方形展示 11月21日增加: 根据@杨泉的建议,也尝试了使用wx.getImageInfo来获取图片的宽高(具体代码可以参考评论区),代码也精简了很多。但是实际比较下来速度要比用image组件慢,初步推测原因是[代码]wx.getImageInfo[代码]会返回本地路径,多了写本地临时文件的时间 ps:用到瀑布流的地方,最好能后端直接返回图片的宽高,省去小程序端获取宽高的麻烦 再ps:我个人并不建议小程序端使用瀑布流
2020-01-14 - 小程序页面(Page)扩展,为所有页面添加公共的生命周期、事件处理等函数
背景 在小程序的原生开发中,页面中经常会用到一些公共方法,例如在页面onLoad中验证权限、所有页面都需要onShareAppMessage设置分享等 假设我们在编码时每个页面都写一遍,显然不是一个高级程序员会干的事情,太Low了。如果我们定义一个公共文件,导出这些公共方法,每个页面都引入,然后再生命周期或者事件处理函数中调用,虽然看起来很方便,但不够优雅,达不到我们最终的目的(偷懒)。 下面给大家介绍一种相对比较优雅的实现方式,扩展Page来实现以上的操作。 Page(页面) 需要传入的是一个 [代码]object[代码] 类型的参数,那么我们重载一个 [代码]Page[代码] 函数,将这个 [代码]object[代码] 参数拦截改掉就可以了,下面直接上代码。 实现 1、在根目录新建一个 [代码]page-extend.js[代码] 文件,公共的逻辑都写在这里面 [代码]/** * * Page扩展函数 * * @param {*} Page 原生Page */ const pageExtend = Page => { return object => { // 导出原生Page传入的object参数中的生命周期函数 // 由于命名冲突,所以将onLoad生命周期函数命名成了onLoaded const { onLoaded } = object // 公共的onLoad生命周期函数 object.onLoad = function (options) { // 在onLoad中执行的代码 ... // 执行onLoaded生命周期函数 if (typeof onLoaded === 'function') { onLoaded.call(this, options) } } // 公共的onShareAppMessage事件处理函数 object.onShareAppMessage = () => { return { title: '分享标题', imageUrl: '分享封面' } } return Page(object) } } // 获取原生Page const originalPage = Page // 定义一个新的Page,将原生Page传入Page扩展函数 Page = pageExtend(originalPage) [代码] 2、在 [代码]app.js[代码] 中引入 [代码]page-extend.js[代码] 文件 [代码]require('./page-extend') App({ // 其他代码 ... }) [代码] 代码片段 https://developers.weixin.qq.com/s/Cyx8iGmV7Ldp 本文内容及评论未经允许,禁止任何形式的转载与复制(代码可在程序中使用)
2019-12-24 - 小程序消息推送实现,含实现代码,和常见问题
最近需要开发微信和小程序的推送功能,需要用java后台实现推送,自己本身java和小程序都做,所以就自己动手实现下小程序的模版推送功能推送。 实现思路1 小程序获取用户openid,收集formid传给java后台 2 java推送消息给指定小程序用户 老规矩,还是先看效果图[图片] 微信收到小程序推送.png 我的这个是跑腿抢单推送,当用户新下单时,会给跑腿员推送消息。 下面开始讲解实现步骤 一,微信小程序管理后台开通模版推送[图片] 模版消息.png 这里的模版id很重要,接下来我们推送的都是这个模版。 二,java后台实现推送所需字段1 看微信官方推送消息所需要的字段 [图片] 实现推送所需要的字段 [图片] 官方示例 2 有了官方说明,我门接下来就去拿到官方所需要的这些字段,来组装请求数据就可以了。 三,下面讲解实现步骤我的java后台是基于springboot开发的,如果你不了解spring boot,建议你先去了解下springboot再回来接着学习。 还有RestTemplate是我们java后台做get和post请求必须的,我们和微信服务器交互就用的RestTemplate 1 首先根据官方推送所需字段组装java-bean 这里用到两个javabean [代码]/* * 小程序推送所需数据 * qcl 微信:2501902696 * */@Datapublic class WxMssVo { private String touser;//用户openid private String template_id;//模版id private String page = "index";//默认跳到小程序首页 private String form_id;//收集到的用户formid private String emphasis_keyword = "keyword1.DATA";//放大那个推送字段 private Map<String, TemplateData> data;//推送文字}[代码][代码]/* * 设置推送的文字和颜色 * qcl 微信:2501902696 * */@Datapublic class TemplateData { //keyword1:订单类型,keyword2:下单金额,keyword3:配送地址,keyword4:取件地址,keyword5备注 private String value;//,,依次排下去// private String color;//字段颜色(微信官方已废弃,设置没有效果)}[代码]到这里请求推送的数据就组装好了,解下来我们去实现推送功能。 奥不对,还有一个重要的字段需要获取到:access_token access_token的获取 [代码]/* * 获取access_token * appid和appsecret到小程序后台获取,当然也可以让小程序开发人员给你传过来 * */ public String getAccess_token(String appid, String appsecret) { //获取access_token String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" + "&appid=" + appid + "&secret=" + appsecret; String json = restTemplate.getForObject(url, String.class); AccessToken accessToken = new Gson().fromJson(json, AccessToken.class); return accessToken.getAccess_token(); }[代码]这次是真正的可以来请求微信服务器来实现消息推送了 [代码]/* * 微信小程序推送单个用户 * */ public String pushOneUser(String openid, String formid) { //获取access_token String access_token = getAccess_token(ConstantUtils.SCHOOL_APPID, ConstantUtils.SCHOOL_APPSECRET); String url = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send" + "?access_token=" + access_token; //拼接推送的模版 WxMssVo wxMssVo = new WxMssVo(); wxMssVo.setTouser(openid);//用户openid wxMssVo.setTemplate_id("LzeDP0G5PLgHoOjCMfhu44wfUluhW11Zeezu3r_dC24");//模版id wxMssVo.setForm_id(formid);//formid Map<String, TemplateData> m = new HashMap<>(5); //keyword1:订单类型,keyword2:下单金额,keyword3:配送地址,keyword4:取件地址,keyword5备注 TemplateData keyword1 = new TemplateData(); keyword1.setValue("新下单待抢单"); m.put("keyword1", keyword1); TemplateData keyword2 = new TemplateData(); keyword2.setValue("这里填下单金额的值"); m.put("keyword2", keyword2); wxMssVo.setData(m); TemplateData keyword3 = new TemplateData(); keyword3.setValue("这里填配送地址"); m.put("keyword3", keyword3); wxMssVo.setData(m); TemplateData keyword4 = new TemplateData(); keyword4.setValue("这里填取件地址"); m.put("keyword4", keyword4); wxMssVo.setData(m); TemplateData keyword5 = new TemplateData(); keyword5.setValue("这里填备注"); m.put("keyword5", keyword5); wxMssVo.setData(m); ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, wxMssVo, String.class); log.error("小程序推送结果={}", responseEntity.getBody()); return responseEntity.getBody(); }[代码]openid可以让小程序开发人员给你传过来,也可以自己获取。 formid需要小程序开发给你传过来,你也可以把formid存到数据库里,什么时候需要直接拿出来用就可以了。 注意:formid必须和用户openid对应。 下面贴出来完整代码[代码]package com.qcl.paotuischool.wechat;import com.google.gson.Gson;import com.qcl.userwechat.bean.AccessToken;import com.qcl.utils.ConstantUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.ResponseEntity;import org.springframework.stereotype.Service;import org.springframework.web.client.RestTemplate;import java.util.HashMap;import java.util.Map;import lombok.extern.slf4j.Slf4j;/** * Created by qcl on 2018/9/11. * 微信小程序推送服务, * 包含获取access_token的服务 */@Service @Slf4j public class WxPushServiceQcl { //用来请求微信的get和post @Autowired private RestTemplate restTemplate; /* * 微信小程序推送单个用户 * */ public String pushOneUser(String openid, String formid) { //获取access_token String access_token = getAccess_token(ConstantUtils.SCHOOL_APPID, ConstantUtils.SCHOOL_APPSECRET); String url = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send" + "?access_token=" + access_token; //拼接推送的模版 WxMssVo wxMssVo = new WxMssVo(); wxMssVo.setTouser(openid);//用户openid wxMssVo.setTemplate_id("LzeDP0G5PLgHoOjCMfhu44wfUluhW11Zeezu3r_dC24");//模版id wxMssVo.setForm_id(formid);//formid Map<String, TemplateData> m = new HashMap<>(5); //keyword1:订单类型,keyword2:下单金额,keyword3:配送地址,keyword4:取件地址,keyword5备注 TemplateData keyword1 = new TemplateData(); keyword1.setValue("新下单待抢单"); m.put("keyword1", keyword1); TemplateData keyword2 = new TemplateData(); keyword2.setValue("这里填下单金额的值"); m.put("keyword2", keyword2); wxMssVo.setData(m); TemplateData keyword3 = new TemplateData(); keyword3.setValue("这里填配送地址"); m.put("keyword3", keyword3); wxMssVo.setData(m); TemplateData keyword4 = new TemplateData(); keyword4.setValue("这里填取件地址"); m.put("keyword4", keyword4); wxMssVo.setData(m); TemplateData keyword5 = new TemplateData(); keyword5.setValue("这里填备注"); m.put("keyword5", keyword5); wxMssVo.setData(m); ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, wxMssVo, String.class); log.error("小程序推送结果={}", responseEntity.getBody()); return responseEntity.getBody(); } /* * 获取access_token * appid和appsecret到小程序后台获取,当然也可以让小程序开发人员给你传过来 * */ public String getAccess_token(String appid, String appsecret) { //获取access_token String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" + "&appid=" + appid + "&secret=" + appsecret; String json = restTemplate.getForObject(url, String.class); AccessToken accessToken = new Gson().fromJson(json, AccessToken.class); return accessToken.getAccess_token(); } }[代码]在需要做推送的地方调用WxPushServiceQcl类中的pushOneUser方法,并传入openid, formid参数即可。 [图片] java控制台打印.png 这是我推送成功后打印的log 下面来讲小程序端开发需要做些什么可以看出,我们的formid有效期是7天,并且一个form_id只能使用一次,所以我们小程序端所需要做的就是尽可能的多拿些formid,然后传个后台,让后台存到数据库中,这样7天有效期内,想怎么用就怎么用了。 官方下发条件 所以接下来要讲的就是小程序开发怎么尽可能多的拿到formid了。 image.png 看下官方提供的,只有在表单提交时把report-submit设为true时才能拿到formid,比如这样 [代码] <form report-submit='true' > <button form-type='submit'>获取formid</button> </form>[代码]所以我们就要在这里下功夫了,既然只能在form组件获取,我们能不能把我们小程序里用到最多的地方用form来伪装呢。 [图片] 红框里是用户常点的 我的小程序是跑腿小程序,消息也主要推送给跑腿员的,而跑腿员点击最多的也就是这两个条目,所以我们就用from组件来伪装这两个条目,让用户在点击的同时就可以收集到用的formid。 [代码] <view class='button_item'> <form class="form_item" bindsubmit='gorRunnerLobby' report-submit='true' data-type="1"> <button class="button" form-type='submit'> <text>抢单大厅</text> <text class='runner_desc'>(兼职也可月入万元)</text> </button> </form> <view class='right_arrow' /> </view>[代码]这样就可以在用户点击条目时,收集到用户formid了 image.png 由于上面的botton有默认样式,所以我们就通过修改css来去除botton默认样式。 [代码].button_item { width: 100%; display: flex; flex-direction: row; align-items: center; padding: 2px 20px; background: white; border-bottom: 1px solid gainsboro; }/* 主要通过这里去除botton默认样式 */.button { width: 100%; background: white; border: none; text-align: left; padding: 6px 0px; margin: 0px; line-height: 1.5; }/* 主要通过这里去除botton边框 */.button::after { border: none; }/* 用button伪装获取formid */.form_item { width: 100%; }[代码]到这里我们小程序端也圆满完成自己的任务了。 这样我们java后台和小程序开发就可以开开心心的完成微信小程序的消息推送功能了。 如果有java或小程序相关的问题可以加我微信交流学习2501902696(备注小程序或java)
2018-09-12 - 小程序即时通讯模板
小程序即时通讯模板,目前已提供WebSocket通信功能示例。 [图片]
2018-08-09