Skip to content
一站式解决 WKWebView 支持离线包,Ajax/Fetch 请求,表单请求和 Cookie 同步的问题 (基于 Ajax Hook,Fetch Hook 和 Cookie Hook)
Objective-C JavaScript TypeScript HTML Python Shell Ruby
Branch: master
Clone or download

Latest commit

Latest commit 5047f44 Jun 19, 2020

Files

Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
KKJSBridge.xcodeproj 补充ajax获取html文本的demo Jun 15, 2020
KKJSBridge 分类ajax和fetch测试demo,并设置统一入口方便统一访问 Jun 18, 2020
KKJSBridgeDemo 更新TODO Jun 19, 2020
.gitignore 不添加node_modules Sep 29, 2019
Demo0.gif 添加demo概览 Jun 18, 2020
Demo1.gif update Aug 31, 2019
Demo2.gif update Aug 31, 2019
Demo3.gif update Aug 31, 2019
Demo4.gif 更新ajax发送表单demo Sep 29, 2019
KKJSBridge.podspec 更新文档 Jun 18, 2020
LICENSE Create LICENSE Aug 29, 2019
README.md 更新TODO Jun 19, 2020
upload.sh 更新脚本,只上传到 master repo Aug 30, 2019
uploadRepo.sh 更新脚本,只上传到 master repo Aug 30, 2019

README.md

KKJSBridge

GitHub stars GitHub forks GitHub issues GitHub license

一站式解决 WKWebView 支持离线包,Ajax/Fetch 请求和 Cookie 同步的问题 (基于 Ajax Hook,Fetch Hook 和 Cookie Hook)

更详细的介绍

KKJSBridge 支持的功能

  • 基于 MessageHandler 搭建通信层

  • 支持模块化的管理 JSAPI

  • 支持模块共享上下文信息

  • 支持模块消息转发

  • 支持离线资源

  • 支持 ajax/fetch hook 避免 body 丢失

  • Native 侧控制 ajax/fetch hook

  • 支持表单数据,支持图片上传(表单虽好,可不要依赖哈)

  • 支持 ajax/fetch 请求外部代理

  • Cookie 统一管理

  • WKWebView 复用

  • 兼容 WebViewJavascriptBridge

Demo

demo 概览

模块化调用 JSAPI

模块化调用 JSAPI

模块化调用 JSAPI

ajax hook 演示

ajax hook 演示

淘宝 ajax hook 演示

淘宝 ajax hook 演示

ajax 发送表单 演示

淘宝 ajax hook 演示

用法

从复用池取出缓存的 WKWebView,并开启 ajax hook

+ (void)load {
    __block id observer = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidFinishLaunchingNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
        [self prepareWebView];
        [[NSNotificationCenter defaultCenter] removeObserver:observer];
    }];
}

+ (void)prepareWebView {
    // 预先缓存一个 webView
    [KKWebView configCustomUAWithType:KKWebViewConfigUATypeAppend UAString:@"KKJSBridge/1.0.0"];
    [[KKWebViewPool sharedInstance] makeWebViewConfiguration:^(WKWebViewConfiguration * _Nonnull configuration) {
        // 必须前置配置,否则会造成属性不生效的问题
        configuration.allowsInlineMediaPlayback = YES;
        configuration.preferences.minimumFontSize = 12;
    }];
    [[KKWebViewPool sharedInstance] enqueueWebViewWithClass:KKWebView.class];
}

- (void)dealloc {
    // 回收到复用池
    [[KKWebViewPool sharedInstance] enqueueWebView:self.webView];
}

- (void)commonInit {
    _webView = [[KKWebViewPool sharedInstance] dequeueWebViewWithClass:KKWebView.class webViewHolder:self];
    _webView.configuration.allowsInlineMediaPlayback = YES;
    _webView.configuration.preferences.minimumFontSize = 12;
    _webView.hybirdDelegate = self;
    _jsBridgeEngine = [KKJSBridgeEngine bridgeForWebView:self.webView];
    _jsBridgeEngine.config.enableAjaxHook = YES; // 开启 ajax hook
    _jsBridgeEngine.config.ajaxDelegateManager = self; // 请求外部代理处理,可以借助 AFN 网络库来发送请求

    [self registerModule];
}

#pragma mark - KKJSBridgeAjaxDelegateManager
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request callbackDelegate:(NSObject<KKJSBridgeAjaxDelegate> *)callbackDelegate {
    // 使用 AFN 发送 ajax 请求 
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    AFURLSessionManager *manage = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
    return [manage dataTaskWithRequest:request uploadProgress:^(NSProgress * _Nonnull uploadProgress) {
        [callbackDelegate JSBridgeAjaxInProcessing:callbackDelegate];
    } downloadProgress:^(NSProgress * _Nonnull downloadProgress) {
        [callbackDelegate JSBridgeAjaxInProcessing:callbackDelegate];
    } completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
        [callbackDelegate JSBridgeAjaxDidCompletion:callbackDelegate response:response responseObject:responseObject error:error];
    }];
}

注册模块

- (void)registerModule {
 ModuleContext *context = [ModuleContext new];
 context.vc = self;
 context.scrollView = self.webView.scrollView;
 context.name = @"上下文";
 // 注册 默认模块
 [self.jsBridgeEngine.moduleRegister registerModuleClass:ModuleDefault.class];
 // 注册 模块A
 [self.jsBridgeEngine.moduleRegister registerModuleClass:ModuleA.class];
 // 注册 模块B 并带入上下文
 [self.jsBridgeEngine.moduleRegister registerModuleClass:ModuleB.class withContext:context];
 // 注册 模块C
 [self.jsBridgeEngine.moduleRegister registerModuleClass:ModuleC.class];
}

模块定义

@interface ModuleB()<KKJSBridgeModule>

@property (nonatomic, weak) ModuleContext *context;

@end

@implementation ModuleB

// 模块名称
+ (nonnull NSString *)moduleName {
    return @"b";
}

// 单例模块
+ (BOOL)isSingleton {
    return YES;
}

// 模块初始化方法,支持上下文带入
- (instancetype)initWithEngine:(KKJSBridgeEngine *)engine context:(id)context {
    if (self = [super init]) {
        _context = context;
        NSLog(@"ModuleB 初始化并带上 %@", self.context.name);
    }

    return self;
}

// 模块提供的方法
- (void)callToGetVCTitle:(KKJSBridgeEngine *)engine params:(NSDictionary *)params responseCallback:(void (^)(NSDictionary *responseData))responseCallback {
    responseCallback ? responseCallback(@{@"title": self.context.vc.navigationItem.title ? self.context.vc.navigationItem.title : @""}) : nil;
}

JS 侧调用方式

window.KKJSBridge.call('b', 'callToGetVCTitle', {}, function(res) {
    console.log('receive vc title:', res.title);
});

安装

  1. CocoaPods

    //In Podfile
    pod "KKJSBridge"

更新历史

2020.6.18

  • 支持 fetch hook

2020.5.18

  • 支持通过 off 方法取消事件监听

2020.3.3

  • 回收 webView 到复用池时,清除 sessionStorage
  • 支持 on 事件广播,让 H5 可以在多个地方注册事件监听

2019.10.23

  • 提供统一配置 configuration 方法,有些属性必须前置配置,否则会不生效

2019.10.22

  • 增加模块注册支持首次初始化

2019.9.29

  • 仅保留 Native 侧控制 ajax hook,主要是避免 ajax hook 时机不对时,会造成首次 hook 失败
  • 支持表单数据,支持图片上传
  • 支持 ajax 请求外部代理

TODO

  • 使用缓存 post body 的思路去做 ajax hook。主要是基于以下几点原因。

    • 目前的响应体是通过 JSBrdige 来进行传输的,性能上肯定没有原生的性能好
    • 对于 ajax 获取二进制数据还不支持,而基于现有的方式去做的话,还是需要写一些编码转换,而这些转换过程是没有那么好控制的
  • 支持 swift

参考

You can’t perform that action at this time.