「大前端」weex裡native主動傳送事件到JS的方案實現

尚妝產品技術刊讀發表於2017-10-30

本文來自尚妝Android團隊路飛
發表於尚妝github部落格,歡迎訂閱!

接入weex已有幾個月,各方面都已慢慢完善。最近遇到一個點,先記錄一下。後續會花時間整理一系列weex相關的文章。希望早點完成。

需求

現在有很多頁面需要在返回的時候重新整理,比如從購物車跳轉到詳情頁加購,再到購物車,這時候應該重新整理頁面;如果從訂單列表頁點選付款跳轉後進行支付後,返回的時候重新整理資料。

viewappear 和 viewdisappear事件

首先想到的是weex已經提供的繫結到根元素的viewappear 和 viewdisappear事件。使用方法是繫結到根元素上,自定義過component的同學在這裡應該不難猜到它是基於fireEvent實現的

  • 1、在Android裡,是在onresume裡傳送viewappear事件,在onpause裡傳送viewappear。
    接入的時候只要在對應的地方呼叫對應weexInstance的resume和pause即可:

    public void onResume() {
    if (wxInstance != null) {
      wxInstance.onActivityResume();
    }
    }複製程式碼
    public void onPause() {
     if (wxInstance != null) {
        wxInstance.onActivityPause();
     }
    }複製程式碼
  • 2、在iOS裡,在viewDidAppear裡傳送viewappear事件,在viewDidDisappear裡傳送事件。這裡可以參考官方demo的WXDemoViewController實現:
    ```

  • (void)updateInstanceState:(WXState)state {
    if (_instance && _instance.state != state) {

      _instance.state = state;
    
      if (state == WeexInstanceAppear) {
          [[WXSDKManager bridgeMgr] fireEvent:_instance.instanceId ref:WX_SDK_ROOT_REF type:@"viewappear" params:nil domChanges:nil];
      }
      else if (state == WeexInstanceDisappear) {
          [[WXSDKManager bridgeMgr] fireEvent:_instance.instanceId ref:WX_SDK_ROOT_REF type:@"viewdisappear" params:nil domChanges:nil];
      }複製程式碼

    }
    }
    -(void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [self updateInstanceState:WeexInstanceAppear];
    }

  • (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    [self updateInstanceState:WeexInstanceDisappear];
    }
    `` 到此,viewappear 和 viewdisappear事件是可以滿足以上返回重新整理的需求的,只要在vue 裡判斷:當觸發viewappear事件時,如果不是第一次觸發,就當做是返回,根據需求,做重新整理請求就可以了。`

還沒結束

由此想到,如果不僅需要這樣的事件,還想給當前頁面,從native發其他事件到js,也不基於介面元素,那麼該如何辦呢?

首先想到的是weex提供的globalEvent,三端都可以傳送事件,接收的時候只要註冊一下就好了。

var globalEvent = weex.requireModule('globalEvent');
globalEvent.addEventListener("geolocation", function (e) {
  console.log("get geolocation")
});複製程式碼

不過,經過實驗會發現,它是全域性的,意思是,一個activity/viewcontroller包含多個weex時,只要註冊了這個事件,那麼就都會收到這個事件。那麼一旦我們傳送的事件名稱出現一樣時,就埋了坑,可能出現事件錯亂。

這裡的解決方案可能會想到規範event name來達到杜絕名稱一樣的情況,不過這更多是一種治標不治本的辦法。

於是乎

於是就有了本文要介紹的方案,讓頁面傳的事件只有自己頁面的js處理。

講道理,這個功能,要是在weex sdk裡實現再好不過了。

原理

說來簡單,該方案基於globalEvent,攜帶instanceId,weex裡通過比對instanceId,只有一致的情況下才進行處理。

Android:

在渲染weex的fragment/activity的onresume裡呼叫:

resumed:成員變數,預設false

if (resumed) {
  Map<String,Object> params = new HashMap<>();
  params.put("id", wxInstance.getInstanceId());
  wxInstance.fireGlobalEventCallback("resume", params);
}
resumed = true;複製程式碼

在自定義module裡增加介面:

@JSMethod(uiThread = true)
public void getInstanceId(final JSCallback callback) {
  if (null != callback) {
     JSONObject jsonObject = new JSONObject();
     jsonObject.put("id", mWXSDKInstance.getInstanceId());
     callback.invoke(jsonObject);
  }
}複製程式碼

iOS:

在渲染weex的viewController裡呼叫:
resumed:成員變數,預設false

-(void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];
    if (self.resumed) {
        [_instance fireGlobalEvent:@"resume" params:@{@"id": _instance.instanceId}];
    }
    self.resumed = true;
}複製程式碼

在自定義module裡增加介面:

-(void)getInstanceId:(WXModuleCallback)callback{
    callback(@{@"id": weexInstance.instanceId});
}複製程式碼

weex 處理:

weex工程裡,封裝一個函式,其中shopBase是自定義的module

const shopBase = weex.requireModule('shopBase');

export default {
  methods: {
    /**
     * config:
     * {
     *  event, name of event 
     *  callback
     * }
     */
    addEventListener(event, callback) {
      if(weex.config.env.platform.toLowerCase() !== 'web') {
        shopBase.getInstanceId((data) => {
            const globalEvent = weex.requireModule('globalEvent');
            const id = data.id;
            globalEvent.addEventListener(event, function (e) {
                if(e.id === id) {
                    callback(e);
                }
            });
        })
      }
    },
  },
};複製程式碼

在需要的頁面呼叫即可,用法同globalEvent:

addEventListener('resume', function (e) {
   shopModal.toast({ message: e.id });
});複製程式碼

相關文章