多页面应用、移动端混合开发H5通信解决方案实践

简介: 移动端混合开发,APP中90%的内容均为内嵌H5,由于种种原因,我在客户端内无法使用单页面路由跳转,只能新开窗口跳转页面,于是被迫形成了“多页面”的情形。(即使是连贯的页面)

0. 背景介绍:

移动端混合开发,APP中90%的内容均为内嵌H5,由于种种原因,我在客户端内无法使用单页面路由跳转,只能新开窗口跳转页面,于是被迫形成了“多页面”的情形。(即使是连贯的页面)

1. 需求场景

例如当处于一个列表中,此时点击某一项跳转至详情进行操作,并改变了这一项的状态,那么列表也需要同步改变该项的状态。(此时列表与详情是两个webview)

最终实现效果:(使用两个标签页模拟真机情况)

xk8ea-wrey4.gif

2. 问题思考

既然是多页面,Vue中的各种通信方式就用不了了,首先想到的是小程序的onShow()生命周期,我们的APP也提供了类似的协议方法,意味着H5页面中可以监听到页面重回,但是该方式依赖原生,且正常情况下也无法做到数据通信,页面重回时直接重新请求新数据会导致整个页面刷新,方案pass。

3. 最终解决方案

localStorage是我前端开发中最常用到的数据持久储存方案没有之一,在同源情况下,完全可以作为多页面之间的数据通信桥梁,我决定采用发布订阅模式,编写一个eventBus,来解耦多页面之间的通信,使用window原生的事件监听充当事件总栈,监听localStorage的变动并广播触发对应操作。下面就一起看看我是如何实现的吧。

4. 实现代码

创建 storageBus.js 文件:

let effects = [];

function depend(obj) { // 收集依赖
  effects.push(obj);
}
function notify(key, params) { // 执行依赖
  const fnList = effects.filter(x => x.name === key);
  fnList.forEach(list => list.fn(JSON.parse(params).data)); // 这里只有data是我们需要的
}

export default {
  $emit(name, data) {
    // 目前定义的数据格式 { data: any but not function, timeStamp: xxx } // timeStamp是为了保持数据能正常监听到更新
    let item = localStorage.getItem(name) || "{}";
    try {
      item = JSON.parse(item);
      item.data = data
    } catch (e) {
      item = {};
    }
    item.timeStamp = new Date().getTime();
    localStorage.setItem(name, JSON.stringify(item));
  },
  $on(name, fn) {
    depend({ name, fn });
  }
};
// 事件总栈:
window.addEventListener(
  "storage",
  e => { // e.key 即为触发的方法,e.newValue 为更新的参数
    notify(e.key, e.newValue);
  }, false );

在一个工具集文件 utils.js 中引入 storageBus

import storageBus from "./storageBus";
export default {
  install(myVue) {
      myVue.prototype.$bus = storageBus;
  }
}

在Vue的入口文件(通常为main)中import这个工具集utils.js,配置 Vue.use(utils);

这样就可以在全局使用 this.$bus 调用我们上面编写的storageBus了;

5. 使用

List.vue 列表中注册事件:

mounted() {
    this.$bus.$on('effect', (data) => {
      // TODO ... data.id ...
    })
  },

Detail.vue 列表跳转详情页某个条件下发送信息:

this.$bus.$emit('effect', { id: xxx })

没错,使用方式和Vue自带的eventBus一致,事件广播,解耦的神。

6. 事件解绑

Vue的eventBus中提供了off方法用于解绑事件,我们也需要实现一个解绑方法,否则页面在多次进入后会重复注册事件,但是重复注册事件也是我们考虑的情况,那么删除特定的注册事件则需要两个判断条件:1. 事件名 2. 事件函数是否一致,这样导致的结果是off方法需要把on注册的匿名函数传递进去,这太麻烦了,为了优雅地解决这个问题,我们可以让on方法在调用过后,主动将off方法吐出,以此来注销对应事件。

storageBus.js 新增代码:

let effects = [];

function depend(obj) { // 收集依赖
  ...
}
function notify(key, params) { // 执行依赖
  ...
}

export default {
  $emit(name, data) { ... },
  
  $on(name, fn) {
    depend({ name, fn });
    return () => {
      this.$off(name, fn);
    };
  },
  
  $off(name, fn) {
    const fnList = effects.filter(x => x.name === name);
    effects = fnList.filter(x => x.fn !== fn);
    if (effects.findIndex(x => x.name === name) === -1) { // 完全不存在对应的函数了就清除数据
      localStorage.removeItem(name)
    }
  }
};

window.addEventListener(
  ...
);

List.vue 列表中注册事件,并在页面销毁时解除:

mounted() {
    this.storageBusOff = this.$bus.$on('effect', (data) => {
      // TODO ... data.id ...
    })
  },
beforeDestroy() {
    this.storageBusOff(); // 销毁了
  }

7. 真机兼容性

经过测试,Ios中storage的监听并不生效,这非常令人遗憾,不过在安卓系统中则正常,但是我在偶然中发现,Ios和Android的webview在页面跳转上并不相同,Ios的webview可以正常进行网页前进后退,安卓则是返回直接关闭了页面(不知道是不是安卓没做处理),总之最后我采用判断环境来使用不同的页面跳转与消息通信:

工具函数:

/**
 * 判断ios,整个生态,包括电脑手机平板
 */
export const isIOS = () => {
  var u = navigator.userAgent;
  var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端
  return isiOS;
};

修改上面的工具集文件 utils.js :

myVue.prototype.$bus = utils.isIOS() ? new Vue() : storageBus; // vue事件广播 与 跨页面事件广播
import router from "../router/index";
// 定义了自己的路由,使用方式与vue-router一致
myVue.prototype.$myRouter = (params, title = null) => {
      if (!utils.isIOS()) {
        const path = params.path ? params.path : params.name ? `/${params.name}` : "";
        let query = "?";
        for (const key in params.query) {
          if (Object.hasOwnProperty.call(params.query, key)) {
            query += `${key}=${params.query[key]}&`;
          }
        }
        query = query.substring(0, query.length - 1);
        xxxxx.appClient({ // 这是混合开发定义的打开页面协议
          type: "openPage",
          data: { url: baseUrl + "saas/dangjian/" + `#${path}` + query }
        });
      } else { // 走正常的路由
        router.push(params);
      }
    };

Vue文件中:

// 使用上两种事件通信没有区别,唯一不同的地方是注销:
beforeDestroy() {
    this.$bus.$off('effect')
    this.storageBusOff instanceof Function && this.storageBusOff();
  }

至此终于解决了使用Vue单页面开发混合app的页面遇到的深坑,在判断函数isIOS中判断的是整个苹果生态,苹果系统则走vue-router路由并且也不是跨页面通信方式,而我的开发电脑是mac,所以整个开发过程还是比较流畅的,网页调试开发的效果基本不用到原生再重新确认一遍,只管发布后提测。

相关文章
|
7月前
|
SQL 安全 算法
移动端安全基础
移动端安全基础
|
3月前
|
编解码 前端开发 JavaScript
前端移动端适配方案
【9月更文挑战第8天】前端移动端适配方案
115 0
|
4月前
|
小程序 前端开发 JavaScript
微信小程序结合PWA技术,提供离线访问、后台运行、桌面图标及原生体验,增强应用性能与用户交互。
微信小程序结合PWA技术,提供离线访问、后台运行、桌面图标及原生体验,增强应用性能与用户交互。开发者运用Service Worker等实现资源缓存与实时推送,利用Web App Manifest添加快捷方式至桌面,通过CSS3和JavaScript打造流畅动画与手势操作,需注意兼容性与性能优化,为用户创造更佳体验。
122 0
|
5月前
|
编解码 前端开发 图形学
【技术深度解析】多平台适配下的UI适配难题:U3D游戏UI错乱的终极解决方案
【7月更文第12天】随着移动设备市场的多元化,Unity游戏开发者面临的一大挑战是如何在不同分辨率和屏幕尺寸的设备上保持UI的一致性和美观性。游戏在高分辨率平板与低分辨率手机上呈现出的UI布局混乱、按钮错位等问题,严重影响玩家体验。本文旨在探讨Unity UI(UGUI)在多平台适配中的最佳实践,通过优化Canvas Scaler设置、灵活运用RectTransform和Anchor Points,以及高效利用设计工具,确保UI的完美适配。
752 1
|
6月前
|
边缘计算 JSON 网络协议
移动端IM开发者必读(三):爱奇艺移动端跨国弱网通信的优化实践
本次分享的文章内容,基于爱奇艺面向全球用户推出的国际版,在海外跨国网络环境复杂的前提下,针对性地做了一系列弱网优化实践,取得了不错的效果,在此总结分享我们的一些做法和优化思路,希望对你有所帮助。
80 1
|
7月前
|
Web App开发 前端开发 JavaScript
构建跨浏览器兼容的前端应用:技术实践与挑战
【5月更文挑战第16天】构建跨浏览器兼容的前端应用是应对浏览器差异和多样性的挑战。使用现代框架(如React、Vue)能自动转换代码,编写可移植的Web标准代码,结合浏览器兼容性测试工具和Polyfill解决旧浏览器支持问题。关注浏览器更新,应对性能、API差异和样式问题,采用渐进增强、条件判断和CSS Reset策略确保应用在各种浏览器上运行良好。
|
7月前
|
小程序 JavaScript 前端开发
微信小程序全栈开发中的数据交互与渲染优化
【4月更文挑战第12天】本文探讨了微信小程序全栈开发中的数据交互与渲染优化,旨在提升小程序性能和用户体验。数据交互涉及GET、POST、PUT和DELETE请求,优化措施包括使用HTTPS、数据压缩、缓存及限流。渲染优化则涵盖虚拟DOM、减少DOM操作、组件化和模板使用,以及WXSS样式设计和媒体查询。利用性能监控工具可识别并优化性能瓶颈。开发者应综合运用这些策略,持续优化小程序。
63 0
|
7月前
|
编解码 前端开发 UED
移动端适配:前端开发的必经之路
【2月更文挑战第1天】移动端适配:前端开发的必经之路
185 0
|
存储 小程序 前端开发
《智能前端技术与实践》——第 2 章 前端开发基础 ——2.7 微信小程序开发——2.7.4 逻辑层文件
《智能前端技术与实践》——第 2 章 前端开发基础 ——2.7 微信小程序开发——2.7.4 逻辑层文件
120 0