查看原文
其他

学而思一对一企业微信应用实战踩坑之路

背景:企业微信这两年在线下场景呈现爆炸性增长趋势,相比个人微信而言,企业微信有很多个人微信无法比拟的优势,比如品牌标识的加强,外部客户的管理,丰富的运营手段等等,说白了企业微信就是面向企业使用的,个人微信是面向个人使用的,定位的不同也决定了企业微信更加适合在运营、营销、推广方面来使用。经过 2021.3 以来的 2 个大版本的产品迭代,目前学而思一对一企业微信已经在用户画像、标签体系建设、裂变活动、渠道活码、群发助手、多主体支持等方面有了生产环境的实践经验。这篇文章分享一下学而思一对一在开发企业微信应用过程中,遇到的坑,以及如何解决的,希望对其他团队和伙伴有一些帮助。

01

微信h5页面返回授权导致循环跳转

在项目中有这样的需求,在home页需要调用微信非静默授权,授权成功后跳转到活动页,然后渲染活动页的数据。在home页调用获取微信授权的全部地址,类似代码获取链接成功后通过window.location.replace (url) 跳转至授权页,用户允许授权后,微信处理页面跳转至链接中的 REDIRECT_URI 地址,这个地址就是活动页。理想情况下,目前window.his tory中只有一个活动页,当页面返回时,页面栈为空,窗口直接关闭。但事实是当用户返回时,会再次返回到home页中, 而home页继续执行跳转授权逻辑,授权成功跳转到活动页,导致用户无法退出活动页,陷入死循环。经过测试发现,在活动页时window.history中存在带code参数的活动页,home页, 如图:在项目中我们期望的效果是当从活动页返回时,直接退出微信窗口。首先解决的思路是当在活动页监听页面返回事件,在返回的回调事件中直接关闭微信窗口。这里就是监听 window 的 popstate 事件,popstate 事件在用户点击回退按钮时,会触发事件,需要注意的是,被激活的历史记录页面必须是通过 window.history.pushState 的方式进入页面栈。代码实现:代码看起来逻辑并不复杂,在手机上测试后发现,在 iPhone 上是我们想要的效果,当页面返回时,直接退出了微信窗口,但是在部分 Android 手机上发现并没有执行,页面还是返回到了 Home 页,只有当用户在活动页操作后,比如点击,长按等动作后,再返回页面,才会触发监听事件。查阅相关文章后,给出的结论是产生原因:微信的安全策略 / 微信浏览器内核解决办法:必须用户点击当前界面(真人交互界面)问题似乎到这里就卡住了,但值得高兴的是:在 iPhone 和部分 Android 已经是我们想要的效果了,剩余的部分安卓手机,只能从其他方面来做兼容。这里先说一下部分文章的解决思路。
  • 引导用户点击页面。在进入页面时弹出弹框,诱导用户点击弹框。这种方法的缺点时如果用户在进入页面后仍不点弹框直接退出,还是会导致页面无法退出,并且并不是所有的页面都适合做弹框。所以该方法并不适合我们的项目。
  • 引入其他第三方库,监听手机原生回退事件,比如引入jQuery mobile,监听手机返回的手势,再执行回调事件。这种方法增加h5页面的大小,不利于页面加载,而且无法保证能监听到所有手机,经测试,在部分手机通过物理按键返回是监听不到的。
如果不能阻止页面返回,那能不能在页面再次返回到home页时,比较两次进入home页的不同,对应关闭窗口?通过对比两次进入home页发现,当用户授权成功后,微信授权跳转到活动页时,会在活动页上带着code参数,拿到授权成功后的code,我们存储在sessionStorage中,当进入home页时,我们先判断是否存在code,如果存在code,就直接退出页面。代码实现:代码注意事项⚠️:需要注意的是这里调用微信关闭窗口的方法时,需要确保WeixinJSBridge已经ready完成,否则在会出现WeixinJSBridge没有加载完成时,已经开始调用closeWindow方法,这时候该方法是不生效的。在手机上重新测试,发现所有手机已呈现我们想要的效果。回顾这个问题,在未处理页面返回之前的逻辑:监听返回,并兼容部分安卓手机后的逻辑:

02

微信h5前端生成海报

在裂变活动页中,需要请求后台接口获取到活动信息,拿到活动海报数据,包括活动背景图,用户昵称,用户头像,企业微信二维码,将这些图片和文字重新生成海报图,插入页面中,用户可通过长按保存海报或者将海报分享给朋友。项目中通过使用 html2canvas 将 DOM 转换成图片,注意事项⚠️:这里使用的是1.0.0-rc.4版本,部分高版本会导致ios手机生成图片空白的问题确定了实现方案后,接下来就是绘制 DOM,查看html2canvas文档,将DOM转化成图片。2.1 跨域问题通过canvas.toDataURL()转化成图片,会涉及到跨域问题,我们期望图片跟当前域名是同源,但我们需要获取用户的微信头像和企业微信活码,所以首先我们需要解决跨域图片的加载问题。1.html2canvas 配置跨域。通过配置useCORS: true(跨域资源共享):通过cors请求图片时一定会带上值为当前域名的Origin请求头,由于使用html2canvas需要先构造DOM,当DOM中有img标签时,会解析img标签的src属性,然后创建Image对象。请求图片的时候,会从浏览器的缓存中读取图片,缓存中的图片没有Access-Control-Allow-Origin响应头,所以如果不处理img,会导致从图片有缓存时生成图片是空白,所以需要给所有的img标签添加crossOrigin="anonymous"属性,即以跨域的方式重新读取图片数据。所以使用CORS我们需要做的是:
  • 配置html2canvas添加useCORS:true
    代码
  • 给DOM中img添加crossorigin=“anonym ous”
    代码
  • 确保图片CDN服务器支持CORS访问,也就是返回Access-Control-Allow-Origin等响应头
  • 图片已经被访问过,即已经存在于缓存中,需要给图片url后添加随机字符串
2.除了使用cors请求图片的方式外,也可以通过将网络图片转化为base64格式。在项目中我们采用将图片转化为base64的方法加载网络图片。代码需要注意的是加载图片时,需要设置图片img.crossOrigin = “Anonymous”,即图片可跨域请求。在开发的过程中,也可以使用loadImage来获取图片base64格式。代码注意事项⚠️:通过两种方式在获取企业微信活码时,在图片后加随机字符串时会导致图片在4G状态下无法请求,与企业微信处理相关2.2 图片清晰度问题
  • 使用 img 代替 background-image 的方式,background-image 会导致图片很糊;
  • 配置 scale 属性,获取设备 DPR,以设备支持的devicePixelRatio 生成图片;
    代码
  • 获取DOM的宽高,赋值给生成的canvas,由于是在移动端生成海报,如果给生成的图片固定的宽,高度按比例,部分机型会导致生成的canvas有白边
2.3 单行文本超出不显示省略号在生成的海报中需要显示用户的微信昵称,当用户的微信昵称过长时,虽然DOM样式中添加了文本超出显示省略号的样式,但是生成的canvas中却无法显示。所以我们需要判断用户昵称的长度,当用户昵称长度超过最大长度时,隐藏超出部分,并通过变量控制显示省略号。代码控制变量的值需要获取页面中完整的用户昵称长度代码2.4 canvas 显示不全在生成canvas的时候,如果出现生成的图片显示不全,很有可能是在绘制的过程中用户滚动了页面的滚动条,导致图片部分内容超出,所以在开始绘制图片前,必须保证页面滚动条置于顶部代码另外我们在将canvas转化为图片时,最好将图片保存为png格式,如果保存成jpg格式图片,会导致部分图片显示黑块,另外保存为png格式,可设置图片的底色为透明度。(经测试,图片底色透明在安卓手机正常,在ios手机则无效)代码

03

容器高度100vh在iPhone上的滚动问题

在测试的过程中我们发现在 iPhone 手机中出现如下情况发现这个 bug 产生于 iOS8 及以上(在 iOS5~7上需要手动使用translateZ(0)打开硬件加速)Safari 对于 overflow-scrolling 用了原生控件来实现。对于有-webkit-overflow-scrolling 的网页,会创建一个UIScrollView,提供子 layer 给渲染模块使用。css样式如下:代码当把高度修改为100%时,发现问题便解决了,高度100vh和100%在移动端有什么区别呢
按照我们的理解,这时的高度应该都是占满屏幕的高度。当把高度设置为100vh时发现已经超过屏幕的高度,也就是说在 iPhone 上 100vh 并不是屏幕的高度,而高度100%则是刚好占满屏幕的高度。
另一种适配 iPhone 的方案是代码
当最小高度不支持-webkit-fill-available时,使用min-height: 100vh,同样能保证页面高度撑满整个屏幕

04

记录页面载入时间埋点

在活动页中,我们需要知道用户进入页面到页面呈现的准确时间,来评估用户的体验。主要涉及到window.performance对象。当用户点击链接或输入链接进入网页到页面内容显示的过程:
  • 前一个页面的unload(如果有前一个页面)
  • http重定向
  • 检查本地缓存
  • DNS域名解析
  • 建立TCP连接
  • 客户端发起请求
  • 服务器响应返回资源
  • 解析dom树
  • 页面加载完成
在整个过程中,每个阶段需要的时间都可在window.performance中查找到对应的字段
  • 重定向耗时:redirectEnd - redirectStart
  • DNS查询耗时 :domainLookupEnd - domainLookupStart
  • TCP链接耗时 :connectEnd - connectStart
  • HTTP请求耗时 :responseEnd - responseStart
  • 解析dom树耗时 :domComplete - domInteractive
  • 白屏时间 :responseStart - navigationStart
  • DOMready时间 :domContentLoadedEventEnd - navigationStart
  • onload时间:loadEventEnd - navigationStart,也即是onload回调函数执行的时间。
对应需求,我们需要获取到 loadEventEnd 的时间和 navigationStart 的时间,在获取loadEventEnd的时间点时,由于设备的不同,我们可能在页面的load回调中获取但仍获取不到,需要在一定的延迟后才能获取到,这里采用定时器去查询的方法去获取值是否存在。代码相比通过window.performance获取页面显示的时间,如果使用页面created的时间,与页面load回调中的时间点的时间差,那记录的时间是缺少页面unload,重定向,建立请求这些时间段的。所以如果需要记录前端的性能,可以考虑使用window.performance的方案。

扫描下方二维码添加「好未来技术」微信官方账号
进入好未来技术官方交流群与作者实时互动~
(若扫码无效,可通过微信号TAL-111111直接添加)
- 也许你还想看 -
好未来直播中台荣获「亚太内容分发大会」RTC技术创新奖
服务网格在网校平台研发部的实践和思考
深入容器之构建大型容器网络

我知道你“在看”哟~


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存