具体问题场景
PC端和移动端需要同时上线图表功能(没有多余工时)
之后的版本迭代(功能、样式、配置等)默认双端同步,开发人员只希望维护一套代码
Echarts在移动端有部分功能不兼容不支持
Echarts在移动端的坑
① 移动端页面使用echarts4 中的地图组件,并添加省份的点击事件,响应click无效,eharts也不支持tap事件。
解决方法:自己代理echarts组件实例的click事件。或更新到echarts5版本
②地图组件有数据的省份高亮状态点击后消失。
解决方法:劫持点击事件做判断
③dataZoom失效
dataZoom:{
type: 'inside',
start: 0,
end: 50,
},
解决方法:把传入的对象参数改成数组
dataZoom:[
{
type: 'inside',
start: 0,
end: 50,
}
],
④ 图表组件的datazoom会阻碍页面的原生滑动事件,导致页面没办法正确上下滑。
解决方法:添加preventDefaultMouseMove属性为false
⑤datazoom为inside时,多个图表在移动端上滑动失效
解决方法:更新到最新版本echarts,但是还是会有部分机型存在这个问题
多端合一是比较理想的解决方案
我们可以看到Echarts在移动端上还是存在很多不兼容的地方。而且Echarts官网时挂着的example都是PC端上的。为了避免各种坑爹问题,我在项目中还是选择了多端为一端的开发方案。
核心思想就是通过Iframe让移动端的页面直接渲染PC的网页,同时微调一些样式以适配移动端的小屏。
图表部分
这部分不是重点,因为页面用的还是PC端的页面。只需要调整部分样式大小就好。主要解决一个留存问题就是Iframe里面图表的内部滑动会影响移动端的页面滑动。
<mobileTouchView @touchInfo="updateTouchInfo" :touchRecord="true">
<...>
<m-dashboard-runtime-item
v-for="item in layout"
:key="item.pkId"
:layout="item"
:field="fieldMap[item.pkId]"
/>
</...>
</mobileTouchView>
解决方法是套了个自定义的滑动层,并监听会出问题的几个操作
<!--mobileTouchView-->
<template>
<div
:class="$style.mobileTouchView"
@touchstart="proxyStart"
@touchmove="proxyMove"
@touchend="proxyEnd"
@touchcancel="proxyCancel"
>
<slot></slot>
</div>
</template>
核心思想是计算touch起点和终点的screenY/screenX的偏离来确定用户手势。具体可以看我的另外一篇推文。
通讯部分
代码分为两部分
移动端部分
主体
<view class="dashboard-page">
....
<!-- 内容 -->
<view class="content" :class="{ showFilter: filterFields.length > 0 }">
<iframe
v-if="iframeUrl"
ref="iframe"
class="webview"
:src="iframeUrl"
></iframe>
<!-- 留白组件 -->
<x-abnormal v-else :text="tips" class="tips" />
</view>
</view>
很简单的一个移动端页面中间嵌套了一个iframe页面
监听加载
async getFormData() {
....
await this.$nextTick();
this.$refs.iframe &&
this.$refs.iframe.addEventListener('load', e => this.handleIframeLoad(e));
}
发送讯息
同时监听PC端发过来的讯息
// iframe加载完成
handleIframeLoad(e) {
// 先打开对PC讯息的监听器
window.addEventListener('message', this.messageGateway);
// 对PC建立握手
this.handShake(5);
}
这里为什么要握手五次?其实这里可以填大一点。因为单方无法知道连接是否成功。 所以每500毫秒重新握手一次,直到收到回复。
握手
handShake(t = 0) {
if (this.connected || t < 1) {
return;
}
// console.log('mobile: 开始建立握手');
this.$refs.iframe.contentWindow.postMessage({
type: 'ping',
data: {timeStamp:this.currentTime},
}, '*');
setTimeout(() => {
this.handShake((t -= 1));
}, 500);
}
收到回复后把this.connected改成true就好了
Pc部分
Iframe通讯
PC上通过监听message来捕抓移动端发送过来的讯息
created() {
window.addEventListener('message', e => this.messageGateway(e));
}
这里可以过滤一下域名:
get allowOrigin() {
return ['localhost:8080', 'm.xxx.com', 'mobile.xxx.com'];
}
messageGateway(e) {
const findIndex = this.allowOrigin.findIndex(item =>
e.origin.includes(item),
);
if (findIndex > -1) {
...
}
}
用户权限问题
在移动端打开PC端的Iframe页面,需要传入token来验证登录状态和身份权限。
get iframeUrl() {
return `${createModuleUrl('app')}/m-dashboard/${this.formId}?token=${
this.token
}`;
}
iframe页面验证token后通过路由跳转到页面
const routes = [
...,
{
path: '/m-dashboard/:formId',
component: () => import('@/views/dashboard/mobile')
},
]