前言

我司在weex上的应用是保证三端统一,为了延续web开发体验,统一在三端的跳转都采用url的形式,即采用<a>组件,或者自定义的openUrl方法进行跳转。

假如现在点击B按钮跳转到/b.html页面,在vue文件中统一书写openUrl('/b.html')。H5中就是简单的调用window.location.href = /b.html,在native中会打开一个视图,然后去下载跟/b.html对应的b.min.jsjs文件,进行原生视图渲染。页面与js文件的映射关系以及如何维护可以翻阅之前的文章查看App的跳转规则的weex支持方案设计。

统一采用url跳转的方式进行页面跳转,为了传参方便,我们也统一采用在url后拼接参数的形式进行页面间传参。

正向传参

不管是weex(native)还是weex(H5)都是通过页面的url进行跳转。

假设案例:

x.com/a.html跳转到x.com/b.html?age=12

1)weex(native) > weex(native) 实例化时从url中取得参数并传入到实例中

weex文档中写到

每个 Weex 页面都有被创建、被销毁两个必经阶段,同时在 Weex 页面运行过程中,native 有机会主动向 Weex 页面发送消息。
Native 有机会在创建一个 Weex 页面的时候,传入一份外部数据,JS 框架也有机会借此机会为相同的 JS bundle 配合不同的 data 生成不同的页面内容。

由上可知native在渲染一个weex页面的时候有机会往这个weex页面传入一个Object类型的数据,数据能通过weex.config取得。

在我司的设计中,native首先会截取跳转的url,然后截取下参数,再把参数传入到weex实例中去。这样我们就能通过weex.config.age进行数据的获取,从而渲染不同的页面内容。

[_instance renderWithURL:[NSURL URLWithString:mstrURL] options:[self SHWeexOptionsWithH5URL:mstrH5URL withURL:mstrURL] data:nil];
2)weex(web)> weex(web)在weex-vue-render层面从url读取参数,写入weex.config

native实现这上述讲到的方式进行数据传递,那么web端也要以相同的方式weex.config.age这种方式去取得页面中的参数age。

本司web端的weex依赖文件是通过webpack打包的方式,所以在requireweex-vue-render依赖后,获取当前url的参数,再存进weex.config对象就好了。

require('weex-vue-render')

// hack 将页面url的参数写入到weex.config中
// app已经有这样的方法,h5自己实现
let urlParamObj = {};
try {
  urlParamObj = utils.parseUrl(window.location.search.slice(1), '&', "=", {maxKeys: 0});  
} catch (error) {
  console.log('--------------weex.js parseUrl---------------------');
  console.log(error);
  console.log('------------------------------------');
}


for (let key in urlParamObj) {
  window.weex.config[key] = encodeURIComponent(urlParamObj[key]);
}

3)native > weex(native) 同理
4)native > weex(web) 同理

对于3、4两种情况,native跳转weex,不管是跳到weex(native)还是跳转到weex(web),都是使用url的形式进行跳转。例:x.com/b.html?age=12。上面也讲到了在weex页面native和web如如何将参数写入weex.config对象中去的。要取得参数,统一在vue中编写weex.config.age便能取得传递进来的age参数。

至此我们统一了三端(ios、android、H5)从A页面到B页面正向的传参方式。

反向传参

1)weexA(native) > weexB(native),weexB页面选择完毕返回weexA页面, weexB(native) > weexA(native)

在提交订单页面我们可以选择优惠券,进入到优惠券使用页面,首先会进行正向传参,因为选择优惠券页面会使用提交订单页的订单数据。

然后在选择优惠券页面选择任意优惠券会回到提交订单页,这时需要携带优惠券数据回去,我们称这个为反向传参。

查看官网我们本可以使用BroadcastChannel这个api去做实例间的数据传递,但是在vue的JS Framework中还不支持这个特性,所以我们暂时使用的是globalEvent这个实例级别的api去代替应用级别的数据传递。

我们首先在公司内部集成的module中增加了fireGlobalEvent方法,在选择优惠券页面调用这个方法。

fireGlobalEvent('getConpon', {
    id: '3323',
  }, function () {
  if (web) {
      
  } else {
    navigator.pop()
  }
})

这个方法首先注册了getCoupon事件,然后传递了数据对象

{
    id: '3323'
}

最后注册了一个回调,当前页面会执行这个回调,回退上一页。

而在上一页(提交订单页),注册了一个事件监听,当这个事件名被触发了,就接收来自这个事件的数据。

const globalEvent = weex.requireModule('globalEvent');
globalEvent.addEventListener('getCoupon', function (e) {
  console.log("get getCoupon")
});

这是业务逻辑中的实现,我们再来看看native为了达到返回上一页并传参效果做了什么处理(android为例)。

public void fireGlobalEvent(String name, String data, final JSCallback callback) {
        SHStorageManager.putToCache(SHWeexConstants.WEEX, SHWeexConstants.NAME, name);
        SHStorageManager.putToCache(SHWeexConstants.WEEX, SHWeexConstants.DATA, data);

        if (null != callback) {
            callback.invoke(new JSONObject());
        }
    }

当在业务中调用fireGlobalEvent方法时,native会把传入的事件名和data存入缓存。然后执行业务中定义的回调函数,而回调中会有navigator.pop()方法,意味着退出当前weex实例进入到上一个页面。

public void onResume() {
        if (wxInstance != null) {
            wxInstance.onActivityResume();

            String data = SHStorageManager.get(SHWeexConstants.WEEX, SHWeexConstants.DATA, "");

            if (!TextUtils.isEmpty(data)) {
                String name = SHStorageManager.get(SHWeexConstants.WEEX, SHWeexConstants.NAME, "");

                try {
                    JSONObject jsonObj = JSONObject.parseObject(data);
                    Map<String, Object> params = new HashMap<>();
                    for (Map.Entry<String, Object> entry : jsonObj.entrySet()) {
                        params.put(entry.getKey(), entry.getValue());
                    }
                    wxInstance.fireGlobalEventCallback(name, params);
                } catch (Exception e) {
                    SHWeexLog.e(e);
                } finally {
                    SHStorageManager.removeFromCache(SHWeexConstants.WEEX, SHWeexConstants.DATA);
                    SHStorageManager.removeFromCache(SHWeexConstants.WEEX, SHWeexConstants.NAME);
                }
            }
        }
    }

然后native会在上一个页面出现时,去缓存中取得之前存入的数据和事件名,再调用官方提供的实例apifireGlobalEventCallback,调用对应的事件,并传数据。

当native中执行了fireGlobalEventCallback这个方法,上一页的事件监听函数就会取得数据。

至此就完成了native中数据的反向传递。

2)weexA(web) > weexB(web), weexB(web) > weexA(web)

反向传递参数在weex的web端就更加好处理了,AB页面都是通过连接后面拼接参数的形式进行传递参数,那么反向传参跟正向传参还是可以按照之前的逻辑进行。

在设计api时特意在回调中会对是否web环境做了判断,因为H5和native在反向传参的行为完全不同,所以判断逻辑会在业务中进行,更方便大家在写业务时进行不同情况不同处理。

fireGlobalEvent('getConpon', {
    id: '3323',
  }, function () {
  if (web) {
      openUrl('/a.html?id=' + '3323');
  } else {
    navigator.pop();
  }
})