一个Bug的修复,促使我写了一个package

发表于:2019-2-19 11:38

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:Kntt    来源:掘金

#
Bug
分享:
  项目地址:https://github.com/Kntt/vue-js-bridge
  主要功能:基于WebViewJavascriptBridge开发的webview-js-bridge的vue插件,后面废话较多,可以直接看项目。如果刚好闲来无事,权当打发时间
  故事背景
  本人前端,在一个移动办公APP团队。由于业务简单,并没有使用RN,WEEX等技术。简单的使用native做了一个壳,主要逻辑都是前端控制。主技术栈是Vue.
  bug出现
  在一段时间内,团队接到多人反馈,APP每天都需要重新登陆。作为团队的前端我很诧异,因为登陆大token在前端保存,有效期是三天,怎么可能会每天都要登陆呢?
  排查原因
  token有效期三天这个肯定没有问题
  团队成员手机都没出过问题,证明逻辑没问题
  收集问题反馈人手机信息,发现所有反馈人员的手机有一个共同点,储存空间都不足(ios),问题也主要集中在苹果手机
  定位问题原因,可能是因为手机储存空间不足,把储存的token清理了。按着这个思路开始排查...
  难道是手机内存空间不足,把储存的token当作没用的信息清理了?(token存在localstorage里面)。
  通过google发现,ios确实存在这种情况,当手机储存空间不足时候会把系统认为无用的信息清理掉,给主要程序提供运行环境。
  问题复现
  找到一台储存空间不足的苹果测试机,登陆到APP后,找一部非常大的电影下载。这时候系统就会清理空间来满足下载电影的需求。结果发现APP需要重新登陆了。问题复现,原因确认。
  解决问题
  一个小APP,仅仅为了储存token而引入sqlite之类的本地数据库,很有些大材小用。但是localstorage,cookie等能储存的地方又都在系统清理的范围内。最后决定把token储存在native端。
  解决方案
  native开发类似localstorage的功能
  token存入native端
  前端启动时查询native端是否存在token,存在就同步到localstorage,不存在,跳转登陆页面。
  其他逻辑不变
  方案确定,一个字,撸起袖子一顿猛干。。。
  坑中有坑
  你说难受不难受...,就不描述经过了,心疼。直接说结论了
  前端和Native端通信采用的是WebViewJavascriptBridge,问题就出在这里,见代码:
   import jsBridge from './utils/native'
  // 省略n行...
  router.beforeEach((to, from, next) => {
  if (to.meta.auth) {
  // 问题出在这里,从APP启动到vue运行到这里,jsBridge实例还未初始化完成,导致jsBridge是undifined
  jsBridge.callHandler('getStorage', {key: 'token'}).then(res => {
  let token = res.body
  if (token && token !== 'null') {
  next()
  } else {
  next('/login')
  }
  store.commit('UPDATE_TOKEN', {token})
  })
  } else {
  next()
  }
  })
  // 省略n行...
  问题已经确定了,是调用jsBridge实例时,它还未初始化完成。感兴趣的可以去看下https://github.com/marcuswestin/WebViewJavascriptBridge源码,虽然初始化时间很短,但是已经造成了问题,APP到这里启动报错。
  继续解决问题:既然未初始化完成,那我们等它完成再用不就完了。有人提议用setTimeout,但是延时多少时间呢?时间短了不一定hold住啊,毕竟不同手机初始化时间还是有差别的。设置长了影响体验,作为一个前端如果用户体验都不顾那还做什么前端,所以这个最容易也是最low的方法被pass了。
  想一个优雅的解决办法
  这就是我写的这个package的初衷,废话说了这么多,进入正题了
  从解决问题的角度出发
  既然使用jsBridge实例时你还没初始化完成,那我们就每次等你初始化完成,把实例回传回来后我再调用,这样就不同担心bridge实例不可用的问题了
  核心代码
   // ...
  init (callback) {
  if (window.WebViewJavascriptBridge) {
  return callback(window.WebViewJavascriptBridge)
  }
  if (window.WVJBCallbacks) {
  return window.WVJBCallbacks.push(callback)
  }
  window.WVJBCallbacks = [callback]
  var WVJBIframe = document.createElement('iframe')
  WVJBIframe.style.display = 'none'
  WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__'
  document.documentElement.appendChild(WVJBIframe)
  setTimeout(function () {
  document.documentElement.removeChild(WVJBIframe)
  }, 0)
  }
  callHandler (payload) {
  let _resolve
  let _reject
  const readyPromise = new Promise((resolve, reject) => {
  _resolve = resolve
  _reject = reject
  })
  this.init(function (bridge) {
  try {
  bridge.callHandler(nativeHandlerName, payload, (response) => {
  _resolve(response)
  })
  } catch (e) {
  _reject(e)
  }
  })
  return readyPromise
  }
  // ...
  习惯了使用Promise的调用方式,顺手把调用方法Promise化处理。
  后续
  为了开发效率,不能一直在APP里面调试,又增加了mock功能,debug功能。
  项目地址:https://github.com/Kntt/vue-js-bridge 如果刚好对你还有些帮助,欢迎star,欢迎pr
  第一次写技术文,第一次写package。写的比较混乱。欢迎指正,讨论。一起进步
  
      上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号