IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 企业微信应用实战踩坑之路 -> 正文阅读

[移动开发]企业微信应用实战踩坑之路

背景

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

1. 微信h5页,页面返回授权导致循环跳转问题

在项目中有这样的需求,在home页需要调用微信非静默授权,授权成功后跳转到活动页,然后渲染活动页的数据。 在home页调用获取微信授权的全部地址,类似

 

https://open.weixin.qq.com/connect/oauth2/authorize?appid=CORPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&agentid=AGENTID&state=STATE#wechat_redirect

获取链接成功后通过?window.location.replace(url)跳转至授权页,用户允许授权后,微信处理页面跳转至链接中的?REDIRECT_URI?地址,这个地址就是活动页。理想情况下,目前window.history中只有一个活动页,当页面返回时,页面栈为空,窗口直接关闭。但事实是当用户返回时,会再次返回到home页中, 而home页继续执行跳转授权逻辑,授权成功跳转到活动页,导致用户无法退出活动页,陷入死循环。

经过测试发现,在活动页时window.history中存在带code参数的活动页,home页, 如图:

在项目中我们期望的效果是当从活动页返回时,直接退出微信窗口。首先解决的思路是当在活动页监听页面返回事件,在返回的回调事件中直接关闭微信窗口。这里就是监听?window?的?popstate?事件,popstate?事件在用户点击回退按钮时,会触发事件,需要注意的是,被激活的历史记录页面必须是通过?window.history.pushState?的方式进入页面栈。代码实现:

 

created() { window.history.pushState({}, null, location.href); window.addEventListener("popstate", () => { wx.closeWindow(); }, false); },

看起来逻辑并不复杂,在手机上测试后发现,在 iPhone 上是我们想要的效果,当页面返回时,直接退出了微信窗口,但是在部分 Android 手机上发现并没有执行,页面还是返回到了 Home 页,只有当用户在活动页操作后,比如点击,长按等动作后,再返回页面,才会触发监听事件。查阅相关文章后,给出的结论是

产生原因:微信的安全策略 / 微信浏览器内核
解决办法:必须用户点击当前界面(真人交互界面)

问题似乎到这里就卡住了,但值得高兴的是:

在 iPhone 和部分 Android 已经是我们想要的效果了,剩余的部分安卓手机,只能从其他方面来做兼容。

这里先说一下部分文章的解决思路。

  • 引导用户点击页面。在进入页面时弹出弹框,诱导用户点击弹框。这种方法的缺点时如果用户在进入页面后仍不点弹框直接退出,还是会导致页面无法退出,并且并不是所有的页面都适合做弹框。所以该方法并不适合我们的项目。
  • 引入其他第三方库,监听手机原生回退事件,比如引入jQuery mobile,监听手机返回的手势,再执行回调事件。这种方法增加h5页面的大小,不利于页面加载,而且无法保证能监听到所有手机,经测试,在部分手机通过物理按键返回是监听不到的。

如果不能阻止页面返回,那能不能在页面再次返回到home页时,比较两次进入home页的不同,对应关闭窗口?

通过对比两次进入home页发现,当用户授权成功后,微信授权跳转到活动页时,会在活动页上带着code参数,拿到授权成功后的code,我们存储在sessionStorage中,当进入home页时,我们先判断是否存在code,如果存在code,就直接退出页面。代码实现:

 

beforeRouteEnter(now, old, next) { next(vm => { let unionId = sessionStorage.getItem("unionId"); if (unionId) { vm.closeWX(); } }); },

 

closeWX() { document.addEventListener( "WeixinJSBridgeReady", function() { WeixinJSBridge.call("closeWindow"); }, false ); //需要jssdk wx.closeWindow(); }

注意事项??:

需要注意的是这里调用微信关闭窗口的方法时,需要确保WeixinJSBridge已经ready完成,否则在会出现WeixinJSBridge没有加载完成时,已经开始调用closeWindow方法,这时候该方法是不生效的。

在手机上重新测试,发现所有手机已呈现我们想要的效果。回顾这个问题,在未处理页面返回之前的逻辑:

监听返回,并兼容部分安卓手机后的逻辑:

2. 微信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
       

      html2canvas(dom, { useCORS: true, canvas, scale: scaleBy, backgroundColor: "rgba(255,255,255,0)" })

    • 给DOM中img添加crossorigin=“anonymous”
     

    <img crossOrigin="anonymous" v-show="posterData.openHeadPortrait" :src="posterData.txAvatar" class="poster-ava" alt="" />

    • 确保图片CDN服务器支持CORS访问,也就是返回Access-Control-Allow-Origin等响应头

  • 图片已经被访问过,即已经存在于缓存中,需要给图片url后添加随机字符串
  1. 除了使用cors请求图片的方式外,也可以通过将网络图片转化为base64格式。在项目中我们采用将图片转化为base64的方法加载网络图片。
 

let canvas = document.createElement("CANVAS"), ctx = canvas.getContext("2d"), img = new Image(); img.crossOrigin = "Anonymous"; // 重点!设置image对象可跨域请求 img.onload = function() { canvas.height = img.height; canvas.width = img.width; ctx.drawImage( img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height ); let dataURL = canvas.toDataURL(outputFormat); canvas = null; resolve(dataURL); }; img.onerror = () => { reject(false); }; // img.src = url; // 旧的方式 img.src = url + "?t=" + new Date().valueOf(); // 防止oss的缓存问题

需要注意的是加载图片时,需要设置图片img.crossOrigin = “Anonymous”,即图片可跨域请求。在开发的过程中,也可以使用loadImage来获取图片base64格式。

 

loadImage(newUrl, { crossOrigin: "Anonymous", canvas: true })

注意事项??:

通过两种方式在获取企业微信活码时,在图片后加随机字符串时会导致图片在4G状态下无法请求,与企业微信处理相关

2.2 图片清晰度问题

  • 使用 img 代替?background-image?的方式,background-image?会导致图片很糊。
  • 配置?scale?属性,获取设备 DPR,以设备支持的devicePixelRatio?生成图片
 

DPR() { // 获取设备dpr if (window.devicePixelRatio && window.devicePixelRatio > 1) { return window.devicePixelRatio; } return 2; },

  • 获取DOM的宽高,赋值给生成的canvas,由于是在移动端生成海报,如果给生成的图片固定的宽,高度按比例,部分机型会导致生成的canvas有白边
 

// 设定 canvas 元素属性宽高为 DOM 节点宽高 * 像素比 canvas.width = parseInt(width) * scaleBy; canvas.height = parseInt(height) * scaleBy;

2.3 单行文本超出不显示省略号

在生成的海报中需要显示用户的微信昵称,当用户的微信昵称过长时,虽然DOM样式中添加了文本超出显示省略号的样式,但是生成的canvas中却无法显示。

所以我们需要判断用户昵称的长度,当用户昵称长度超过最大长度时,隐藏超出部分,并通过变量控制显示省略号。

 

<span class="poster-text-nickname"> {{posterData.txNickName}} </span> <span>...</span>

控制变量的值需要获取页面中完整的用户昵称长度

 

<div style="height:0;"> <span class="nickname"> {{ posterData.txNickName }} </span> </div>

2.4 canvas 显示不全

在生成canvas的时候,如果出现生成的图片显示不全,很有可能是在绘制的过程中用户滚动了页面的滚动条,导致图片部分内容超出,所以在开始绘制图片前,必须保证页面滚动条置于顶部

 

window.pageYOffset = 0; document.documentElement.scrollTop = 0; document.body.scrollTop = 0;

另外我们在将canvas转化为图片时,最好将图片保存为png格式,如果保存成jpg格式图片,会导致部分图片显示黑块,另外保存为png格式,可设置图片的底色为透明度。(经测试,图片底色透明在安卓手机正常,在ios手机则无效)

 

this.imgUrl = canvas1.toDataURL("image/png");


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

在测试的过程中我们发现在 iPhone 手机中出现如下情况

发现这个 bug 产生于 iOS8 及以上(在 iOS5~7上需要手动使用translateZ(0)打开硬件加速)

Safari 对于?overflow-scrolling?用了原生控件来实现。对于有-webkit-overflow-scrolling?的网页,会创建一个UIScrollView,提供子 layer 给渲染模块使用。css样式如下:

 

height: 100vh; overflow-y: scroll; position: relative; -webkit-overflow-scrolling: touch; background: #ffffff;

当把高度修改为100%时,发现问题便解决了,高度100vh和100%在移动端有什么区别呢
按照我们的理解,这时的高度应该都是占满屏幕的高度。

当把高度设置为100vh时发现已经超过屏幕的高度,也就是说在 iPhone 上 100vh 并不是屏幕的高度,而高度100%则是刚好占满屏幕的高度。
另一种适配 iPhone 的方案是

 

min-height: 100vh; /* mobile viewport bug fix */ min-height: -webkit-fill-available;

当最小高度不支持-webkit-fill-available时,使用min-height: 100vh,同样能保证页面高度撑满整个屏幕


4.记录页面载入时间埋点(window.performance)

在活动页中,我们需要知道用户进入页面到页面呈现的准确时间,来评估用户的体验。主要涉及到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回调中获取但仍获取不到,需要在一定的延迟后才能获取到,这里采用定时器去查询的方法去获取值是否存在

 

pageLoadTime() { window.addEventListener("load", () => { this.$nextTick(() => { let performance = window.performance || window.msPerformance || window.webkitPerformance; if (performance) { if (performance && performance.timing) { let timer = setInterval(() => { if ( performance.timing && performance.timing.loadEventEnd !== 0 ) { clearInterval(timer); let loadTIme = performance.timing.loadEventEnd - performance.timing.navigationStart; } }, 200); } } }); }); },

相比通过window.performance获取页面显示的时间,如果使用页面created的时间,与页面load回调中的时间点的时间差,那记录的时间是缺少页面unload,重定向,建立请求这些时间段的。所以如果需要记录前端的性能,可以考虑使用window.performance的方案。


作者介绍:

郭贝贝,学而思一对一前端开发工程师,四年前端研发经验,喜爱前端,绘画,擅长angular, vue。

?

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-07-10 14:40:06  更:2021-07-10 14:40:53 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 7:41:42-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码