如何实现一个优雅的jsBridge

什么是jsbridge

jsbridge是客户端和H5沟通的桥梁,经过它,咱们能够获取部分原生能力,同时客户端也能够使用咱们提供的一些方法。实现双向通讯。javascript

jsbridge原理

客户端能够经过webview里面注入一些javascript的上下文,能够理解为在window对象上挂载了一些方法,而后H5经过特定的对象能够获取到这个方法,反过来也是同样,js挂载了一些方法到window对象上,客户端也就能够调用js的某些方法。前端

具体实现

方案一:注入API

IOS UIWebView
JSContext *context = [uiWebView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

context[@"postBridgeMessage"] = ^(NSArray<NSArray *> *calls) {
    // Native 逻辑
};
复制代码

H5使用window.postBridgeMessage(message)调用java

IOS WKWebView
@interface WKWebVIewVC ()<WKScriptMessageHandler>

@implementation WKWebVIewVC

- (void)viewDidLoad {
    [super viewDidLoad];

    WKWebViewConfiguration* configuration = [[WKWebViewConfiguration alloc] init];
    configuration.userContentController = [[WKUserContentController alloc] init];
    WKUserContentController *userCC = configuration.userContentController;
    // 注入对象,前端调用其方法时,Native 能够捕获到
    [userCC addScriptMessageHandler:self name:@"nativeBridge"];

    WKWebView wkWebView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];

    // TODO 显示 WebView
}

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    if ([message.name isEqualToString:@"nativeBridge"]) {
        NSLog(@"前端传递的数据 %@: ",message.body);
        // Native 逻辑
    }
}
复制代码

H5使用window.webkit.messageHandlers.nativeBridge.postMessage(message)方式调用。android

来看一下H5端具体实现ios

假设咱们须要一个getUserInfo方法,用于H5获取当前APP登陆用户的信息。web

那么咱们js能够这样:promise

import registerCallback from '../registerCallback';

export default function getUserInfo() {
  return new Promise((resolve, reject) => {
    try {
      window.webkit.messageHandlers.getUserInfo.postMessage({
        callback: registerCallback(resolve),
      });
    } catch (e) {
      reject(e);
    }
  });
}
复制代码

咱们定义一个getUserInfo方法,这个方法会去调用window.webkit.messageHandlers.getUserInfo.postMessage方法,这是客户端写入的方法,而后传入一个callback方法。客户端会经过callback方法把咱们须要的信息返回给咱们。bash

咱们能够再看下registerCallback这个方法是什么:微信

window.knCallbacks = {};

function makeRandomId(func) {
  return `${func.name || 'anonymous'}_${Date.now()}`;
}

export default function registerCallback(callback, keepAlive) {
  if (!callback) {
    return null;
  }

  const callbackId = makeRandomId(callback);
  window.knCallbacks[callbackId] = (data) => {
    let result;
    if (typeof data === 'object') {
      result = data;
    } else if (typeof data === 'string') {
      try {
        result = JSON.parse(data);
      } catch (e) {
        result = data;
      }
    }
    callback(result);
    if (!keepAlive) {
      delete window.knCallbacks[callbackId];
    }
  };

  return `knCallbacks.${callbackId}`;
}
复制代码

在这个方法里面,咱们会为promise的resolve方法生成一个随机函数名,避免出现window可能会有同名函数致使将其覆盖的问题。 而后咱们根据生成的函数名重写一个函数,将这个函数最后返回给外部的callback,传递给了客户端。 这里的data就是客户端调用callback传递过来的数据。 咱们在这里对数据作一些处理,而后给到callback,也就是promise的resolve。dom

Android

客户端实现:

publicclassJavaScriptInterfaceDemoActivityextendsActivity{
  private WebView Wv;

  @Override
  publicvoidonCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    Wv = (WebView)findViewById(R.id.webView);
    final JavaScriptInterface myJavaScriptInterface = new JavaScriptInterface(this);
    Wv.getSettings().setJavaScriptEnabled(true);
    Wv.addJavascriptInterface(myJavaScriptInterface, "knJSBridge");
    // TODO 显示 WebView
  }

  publicclassJavaScriptInterface{
    Context mContext;
    JavaScriptInterface(Context c) {
        mContext = c;
    }
    publicvoidpostMessage(String webMessage){
        // Native 逻辑
    }
  }
}
复制代码

H5实现

import registerCallback from '../registerCallback';

export default function getUserInfo() {
  return new Promise((resolve, reject) => {
    try {
      window.knJSBridge.getUserInfo(JSON.stringify({
        callback: registerCallback(resolve),
      }));
    } catch (e) {
      reject(e);
    }
  });
}
复制代码

这里咱们其实能够看到,knJSBridge实际上是咱们和客户端约定的一个字段。而后咱们经过window.knJSBridge就能够调用客户端的方法了。 registerCallback和IOS的是同样的。

根据上面咱们H5这里最后能够实现这样一个结构的jsbridge

jsbridge
  - index.js
  - ios/
    - index.js
    - getUserInfo.js
  - android/
    - index.js
    - getUserInfo.js
复制代码
// jsbridge/index.js
import iosBridge from './iOs';
import androidBridge from './android';

export default class Bridge {
  constructor() {
    super();
    if (isAndroid) {
      this.jsbridge = androidBridge;
    } else {
      this.jsbridge = iosBridge;
    }
  }
  getUserInfo = (...args) => this.jsbridge.getUserInfo(...args);
}

// ios/index.js
import getUserInfo from './getUserInfo';

export {
  getUserInfo
}

// ios/getUserInfo.js
import registerCallback from '../registerCallback';

export default function getUserInfo() {
  return new Promise((resolve, reject) => {
    try {
      window.webkit.messageHandlers.getUserInfo.postMessage({
        callback: registerCallback(resolve),
      });
    } catch (e) {
      reject(e);
    }
  });
}

// android/index.js
import getUserInfo from './getUserInfo';

export {
  getUserInfo
}

// android/getUserInfo.js
import registerCallback from '../registerCallback';

export default function getUserInfo() {
  return new Promise((resolve, reject) => {
    try {
      window.webkit.messageHandlers.getUserInfo.postMessage({
        callback: registerCallback(resolve),
      });
    } catch (e) {
      reject(e);
    }
  });
}
复制代码

方案二:url拦截

这种方案就是H5构造一个iframe,经过给iframe设置src发起请求,而后客户端拦截请求实现。

通常会将url设为一个特殊的字符串,好比https://__bridge__这个样子,而后后面跟上咱们须要传递的数据,包括一个callback函数名,客户拦截这种请求,而后经过callback将数据传递回来。

前端收藏家(微信号: fedaily)

收集全网优秀前端技术资讯,与你分享,共同成长。
相关文章
相关标签/搜索