微信小程序开发中的二三事之网易云信IMSDK DEMO

2018-12-11  本文已影响42人  yijian2595

本文由作者邹永胜授权网易云社区发布。

简介

为了更好的展示我们即时通讯SDK强悍的能力,网易云信IM SDK微信小程序DEMO的开发就提上了日程。用产品的话说就是:

  • 云信 IM 小程序 SDK 的能力演示

  • 提供开发者小程序开发参考

换句话说就是在微信里面通过我们云信的IM SDK再实现一个mini版微信。整个小程序主要功能点总的来说是:

废话不多说直接上图:

一期已经上线,不足的地方,恳请斧正

本文从基础开始介绍在开发云信DEMO的过程中的一些难点、整体的结构设计、思考的一些解决方案以及踩过的一些坑,希望对大家有些帮助当然希望更多人接入网易云信SDK

基础

小程序开发基本零门槛,难度基本与模板语言相当,如果你有使用MVVM框架开发前端的经验,基本花个半小时过一遍微信小程序官方文档,即可入门,具体开发细节可以边做边查本人就是这样的。。。

首先需要明白小程序的运行环境,它运行在微信的上下文中,处于微信这个沙盒中,没有window对象,不能访问基于browser context下的DOM。在ios设备上是运行在JSCore(苹果开发),在android设备上则是在X5(腾讯基于webkit开发),在开发工具中运行在nwjs(同类型还有electron)

一个标准的小程序是由一个应用实例以及多个页面实例构成。仔细想来微信小程序不就是由多个相互关联的页面组成的嘛,在每个页面中,需要考虑与外部以及与其他页面进行交互。本着“3W+1H”原则,因此也就可以提炼出在开发整个IM DEMO过程中需要关注的点:

0x01 静态页面

在微信官网下载开发工具,然后新建一个小程序工程,会发现项目根目录下会有一个 app.json和project.config.json以及pages/logs 目录下的 logs.json,这里来阐述下它们的区别:

阐述完各种配置文件之后,可以开始进行页面的编程。传统的网页编程采用的三剑客 HTML+CSS+JS来实现,在微信小程序中同样有三剑客 WXML+WXSS+JS。

0x02页面间通信

整个小程序是由多个页面组成,有时候会遇到需要跨页面进行通信的场景,例如聊天跳转到聊天界面、删除、拉黑好友后通知外部进行好友列表的刷新等等。思考后有如下几种方式可供参考:

要想满足耦合性小、不局限于页面跳转通信、通信数据量不受限制这些需求,很明显发布/订阅模式(观察者模式)符合我们的要求。既能做到时间上解耦又能做到对象间解耦。iOS端的Notification Center以及android端的EventBus都是通过这一设计模式来处理跨页面间通信的的需求的。然后微信小程序内部并没有集成这一事件通知机制,因此需要手动去实现一个并将其与微信小程序的页面生命周期结合起来。

观察者模式是由调度中心、发布者、订阅者组成。订阅者会先在调度中心订阅某一特定事件并注册对应的回调函数,当发布者发布了该事件后,订阅中心就会取出订阅了该事件的所有订阅者注册的回调函数进行执行。

观察者模式不难实现,重点是如何在微信小程序中搭配其特有的生命周期来使用。本项目在用户登录以及注册成功时会初始化消息订阅中心,并全局(globalData)保存,使得订阅器一直驻存在内存中,调用时直接从globalData调用即可。当然这其中还存在一些小问题,在页面进行切换时需要注意订阅者、发布者之间的时序,比如订阅早于发布或者发布之后还未订阅的情况。后期会详细介绍该种模式的实现过程敬请期待。

0x03交互事件

传统的DOM事件传递类型有冒泡型与捕获型,微信小程序中自然也有。通常会使用bind、catch(冒泡)和capture-bind、capture-catch(捕获)前缀来装饰具体的交互事件,两者的区别如下:

而小程序支持的事件类型与传统的H5的差不多,新增了长按事件以及css动画相关触发(类似于Vue的js动画钩子)事件,具体为触摸事件touchstart、touchmove、touchcancel、touchend、tap;长按事件longpress、longtap;动画相关事件transitionend、animationstart、animationiteration、animationend;3Dtouch事件touchforcechange

整个事件命名还是较为清晰,基本做到了见名知意,详细可以查看文档

页面跳转时触发的钩子以及Page实例的生命周期,请自行查看官网,这里不再赘述,这部分内容同样重要。

说完了事件,肯定要说事件传参了,方法主要有两种:

0x04 自定义组件

从小程序基础库版本 1.6.3 开始,它支持了组件化编程。组件类似于每一个页面,同样由四个文件构成json、wxml、wss、js,只在js中默认的一些钩子函数变化了、json中定义变化了,wxml和wss基本类似。多说一句,组件wxss中不应使用ID选择器、属性选择器和标签名选择器被你干了这么多,还剩啥。。。下面就来展开讲讲

对于json文件的话需要将 component 字段设为 true

对于wxml文件,它的写法与页面模板相同。组件模版与数据拼接后生成的节点树,将被插入到组件的引用位置上。但是组件模板多出一个功能就是:支持slot。用过vue的对它肯定十分熟悉,在制作容器组件(承载组件使用者提供的wxml结构)时用起来十分方便。同时它还支持多插槽(name区分),只需在js文件中声明下即可。

对于wxss文件,写法与css类似,只是有几点区别:作用域仅仅局限于组件内;只使用class选择器(其他选择器要么不支持,要么在特殊情况下会有问题);除了继承样式外,例如font、color等,全局样式(app.wxss)对自定义组件无效。至于外部引入样式则从 1.9.90 基础库才开始支持。。。

对于js文件则与前面的页面类似,整个js文件基本就是一个自定义组建的构造器,调用构造器可以指定组件的属性、数据、方法等。比较常用的有:

组件实例this可以自组件方法、生命周期、属性observer中访问。通过组件实例可以获取许多有用的属性和方法,例如is(组件文件路径)、triggerEvent(触发事件,外部可监听)、setData(设置data并渲染视图)等

了解了组件的实现过程,接下来就是使用。用法很简单,只需在json文件中声明组件,然后在wxml中引入使用即可。

// index.json 引入组件,并定义引用名字{  "usingComponents": {    "input-modal": "/path/to/inputmodal"
  }
}// index.html 引入组件并传入属性以及监听事件<input-modal title="输入提醒"     catch:inputModalClick="tipClickHandler">
    <view>内部slot</view>
    </input-modal>// index.js 实现事件监听函数Page({
    tipClickHandler(e) {
        console.log('自定义组件事件');
    }
})

工程结构

整个微信小程序DEMO目录结构如下:

|- components 自定义组件目录
|- images 项目中使用的一些高频次图片
|- pages 主功能一级页面
    |- contact 通讯录页
    |- login 登录页
    |- recentchat 最近会话页
    |- register 注册页
    |- setting 设置页
|- partials 二级页面
    |- addfriend 添加好友页
    |- blacklist 黑名单页
    |- chatting 聊天页
    |- forwardcontact 转发消息通讯录页 
    |- historyfromcloud 云端历史记录页
    |- messagenotification 消息通知中心页
    |- modify 修改个人资料页
    |- personcard 非陌生人个人名片页
    |- strangercard 陌生人个人名片页
|- utils 存放一些工具类js
    |- config.js 存放项目的基本配置
    |- emojimap.js emoji文本与对应图片的映射关系,自定义emoji组件使用
    |- event.js 观察者模式具体实现
    |- imageBase64.js 存储一些小图标bese64编码
    |- imeventhandler.js 网易IM SDK初始化以及对应的回调函数注册,通过消息发布、订阅与外部通信
    |- pinyin.js 获取汉字的拼音
    |- util.js 一些工具方法的集合
|- vendors 引入外部的库,主要有网易云信 IM 的SDK以及md5加密
|- app.js 小程序根实例,存储了全局中的一些数据
|- app.json 注册页面以及定义页面一些基本样式
|- app.wxss 全局样式
|- project.config.json 设置整个小程序工程的一些属性,包括编译类型(截止2018年3月新增加了微信插件)、基础库版本等

技术栈的一些思考

这里探讨下目前(截止2018年3月)比较流行的三种开发微信小程序的方式:微信小程序原生、wepy、mpVue


微信小程序 wepy mpvue
开发规范 小程序开发规范 vue开发规范 vue开发规范
状态管理 vuex
组件化 比较原始 自定义组件规范 vue组件
多端复用 不可 可转化为H5 可转化为H5
构建方式 开发工具内置自动构建 框架内置 webpack
构建原理 开发工具自动构建 构建为dist后转化为小程序支持类型然后将开发工具指向dist目录,支持热更新 构建为dist后转化为小程序支持类型然后将开发工具指向dist目录,支持热更新

接着分析下云信IM DEMO的需求,首先受限于同一设备下一个用户的Storage的上限为10MB,所以这边不做聊天数据的持久化,所有的聊天数据、用户数据存储在内存中,在小程序被微信关闭(驻留后台过久)或者用户手动关闭(杀了微信进程)时所有数据会被重置;其次本期需求主要为p2p单聊,后期还会添加上群聊功能等功能,所以这边整体代码量需要控制,不能引入非必要框架;本期需求支持的消息类型有文本、emoji、地图、视频、语音、图片,部分组件可以借助微信提供的能力,加速渲染。。。

接下来大致评估下实现每个页面的技术点

一级页面:

二级页面:

初步结合框架特点以及几大开放方式特性,矛头重点集中在如何解决应用状态管理上面,经过评估后功能点较多,因此需要尽可能的减少引入外部框架,所以这边在微信小程序的基础上实现全局存储一个消息订阅器,然后在每个功能页面中订阅相应的事件,在相应的地方发布对应的事件。这样就解决了状态管理这个痛点。对于其他的一些区别个人觉得没有任何问题,对于一个有过现代前端开发经验,有使用过mvvm框架经验的开发者来说,入门小程序也就是几个小时的时间本人就是。既然花个几个小时能够入门小程序原生开发,为何还要去选那些坑较多,入门时间相同的框架呢。。。

因此制定了如下开发原则:尽量采用微信提供的原生组件,减少引入外部组件,手撸项目中所需的自定义组件,全局存储数据。页面间采用观察者模式进行通信

观察者模式

常规的观察者模式实现起来并不复杂,总结来说就是:订阅器中存储了所有订阅者注册的所有回调函数,当事件发生时,订阅器就会循环遍历所有的订阅者,并找出订阅该事件的订阅者所注册的回调函数并执行;取消订阅则是重订阅者数组中删除对应的回调函数。

结合在小程序中使用就是在一开始初始化登录组件时就初始化消息订阅器,并将其保存在全局数据(globalData)中,这样全局就驻留了该对象,在各个页面中就可以轻松调用订阅器中的订阅、发布方法来实现通信了。

// 订阅function _bind(eventName, callback, isOne, context) {  /* eslint valid-typeof: 0 */
  if (typeof eventName !== 'string' || typeof callback !== 'function') {    throw new Error('args: ' + stringStr + ', ' + functionStr + '')
  }  if (!hasOwnKey(__onfireEvents, eventName)) {
    __onfireEvents[eventName] = {}
  }
  __onfireEvents[eventName][++__cnt] = [callback, isOne, context]  return [eventName, __cnt]
}// 发布function fire(eventName) {  // fire events
  var args = slice(arguments, 1)
  setTimeout(function() {    if (hasOwnKey(__onfireEvents, eventName)) {
    _each(__onfireEvents[eventName], function(key, item) {
      item[0].apply(item[2], args) // do the function
      if (item[1]) delete __onfireEvents[eventName][key] // when is one, delete it after triggle
    })
  }
  })
}

上面是整个观察者模式的核心:订阅、发布,当然如果你还想继续完善,可以尝试增加命名空间来防止事件名冲突以及增加离线事件支持。

自定义组件

微信小程序自定义组件比较简单,详情可以查看。这里就以聊天界面中使用的自定义emoji组件举例,来阐述如何实现一个自定义组件。

组件的定义方式,以及对应的生命周期钩子这边就不再说明,请查阅上面文档。本组件借助了小程序提供的swiper组件(省的自己判断scroll的位置来切换页面),每个swiper-Item里面再通过模板循环出每张emoji图片,而每个emoji的key对应线上地址则由自己提前准备好的一个已经抽象好的js提供,每组swiper内含有的emoji数量则通过程序自动分割,并在最后添加删除按钮。

// 为每页分配对应的emojifunction splitAlbumKeys(arr, space, currentAlbum) {  const delta = space || 23
  let result = []  let factor = Math.ceil(arr.length / delta)  let begin = 0
  let end = 1
  if (factor == 1) {
    result = [[...arr]]
  } else {    for (let i = 1; i < factor; i++) {      let temp = []
      temp = [...arr.slice(begin, i * delta)]
      begin = i * delta
      result.push(temp)
    }
    result.push([...arr.slice(delta * (factor - 1), arr.length)])
  }  if (currentAlbum == 'emoji' || this.data.currentAlbum == 'emoji') { // 只有emoji才添加删除按钮
    result.map((cata, index) => {      if(index != (result.length-1)) {
        cata.push('[删除]')
      }
    })
  }  return result
}// 单击emoji,向外传递事件function emojiTap(e) {  let emoji = e.target.dataset.emoji  if (!emoji) return
  this.triggerEvent("EmojiClick", emoji)
}// 单击发送,向外传递事件function emojiSend () {    this.triggerEvent("EmojiSend")
}

在外部使用过程中,只需要监听对应的事件即可.

<component-emoji bind:EmojiClick="emojiCLick" bind:EmojiSend="emojiSend"></component-emoji>

遇到的一些坑

微信小程序开发可以说是在坑中前行,经常会遇到一些很奇怪的问题,在此记录在册,希望后来人可以跳过,增加开发效率

这边仅仅是抛出一些我开发中遇到的部分问题,在整个开发过程中踩过的坑远远不止这些,希望我们一起在坑中前行。。。

写在最后

初步统计本项目大概8500行代码(去除IM SDK以及MD5加密库),换句话说不到9000行代码,你就能在微信中实现一个mini 微信,这一切均借助于云信IM SDK强大的即时通讯能力。当然本期版本还存在很多不足的地方,希望在做第二期群聊功能的时候,能继续升级整体的组织架构。

网易云信IM免费试用请点击这里

更多网易技术、产品、运营经验分享请访问网易云社区

相关文章:
【推荐】 基于Impala平台打造交互查询系统
【推荐】 从疑似华住集团4.93亿开房信息泄露 看个人如何预防信息泄露

上一篇 下一篇

猜你喜欢

热点阅读