react native之WebView中注入js接口(jsBridge)

前言

在react native之前舍咖,大都采用hybird方案球订,目前WebView已經是app中不可或缺的一部分祈搜,采用react native之后依然需要支撐婚度。react native核心庫中就帶有WebView的封裝寻咒,但只是最基礎支撐,要擴展WebView的功能奋献,手段之一就是注入js钦听,俗稱jsBridge。

react native需要iOS7以上系統支撐叼架,因此注入js有兩種方案:

  1. 通過Request Url截獲解析。這是在iOS7之前采用的方式。
  2. 通過系統提供的javascriptCore通信方式。

這里我們討論第二種方案仪召,如果你對jsBridge不太熟悉咖摹,可以看這篇H5與native之間的通信。如果對javascriptCore不熟悉彼棍,可以看這個javascriptCore詳解

既然已經有成熟的方案弛作,為什么還要寫這篇文章涕蜂?

還是那句話,最好不要修改react native原有代碼映琳,對以后的版本控制以及維護都不好宇葱,下面就來看看如何不修改react native實現需求,先放出項目地址刊头。

JS注入實現

要給WebView注入js,需要WebView資源加載完畢時诸尽,獲取WebView的JSContext原杂。也就是在- (void)webViewDidFinishLoad:(UIWebView *)webView回調方法中通過[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]獲取上下文,然后就可以為所欲為了您机。

那么關鍵點還是在:如何不侵入react native內部源碼穿肄。

這時候category和swizzling隆重登場。首先使用swizzling替換原有webViewDidFinishLoad方法:

+(void)load {
  
  RCTSwapInstanceMethods([RCTWebView class], @selector(initWithFrame:), @selector(newInitWithFrame:));
  RCTSwapInstanceMethods([RCTWebView class], @selector(webViewDidFinishLoad:), @selector(newWebViewDidFinishLoad:));
}

然后在新方法中际看,除了執(zhí)行原有邏輯之外咸产,再執(zhí)行js注入:

- (void)newWebViewDidFinishLoad:(UIWebView *)webView {
  
  [self injectWebView: webView];
  [self newWebViewDidFinishLoad: webView];
}

-(void) injectWebView: (UIWebView *) webView {
  
  JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
  if (context == nil) {
    return;
  }
  
  //自定義注入對象
  __weak typeof(self) weakSelf = self;
  context[@"alert"] = ^(NSString *message) {
    
    dispatch_async(dispatch_get_main_queue(), ^{
      
      UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"" message: message delegate:weakSelf cancelButtonTitle:@"cancel" otherButtonTitles:nil, nil];
      [alert show];
    });
    
  };
}

這里定義了個簡單的函數alert,用于調用native方法仲闽。下面驗證下注入是否有效脑溢,在js側定義WebView,并自動調用alert方法赖欣,為簡單起見WebView加載本地html:

const HTML = `<html>

  <body>
    <h1>Hello web view</h1>
    <input type="button" value="call native" onclick='buttonAction()'/>
    <script>
      function buttonAction() {
        alert('native button alert');
      };
      alert('native alert');
    </script>
  </body>
</html>`;

  <WebView
    ref={'webView'}
    automaticallyAdjustContentInsets={false}
    // style={styles.webView}
    source={{html: HTML}}
    // source={{url:'https://www.baidu.com'}}
    javaScriptEnabled={true}
    domStorageEnabled={true}
    decelerationRate="normal"
    onNavigationStateChange={this.onNavigationStateChange}
    onShouldStartLoadWithRequest={this.onShouldStartLoadWithRequest}
    // startInLoadingState={true}
    scalesPageToFit={true}/>

看下效果:

單獨提一下屑彻,iOS中的WebView每次finishLoad時JSContext都會發(fā)生變化,所以要在每次load結束時重新注入js顶吮。還有一種情況是社牲,在資源加載過程中需要調用native接口,那么就要在WebView創(chuàng)建時同時獲取JSContext注入js:

- (instancetype)newInitWithFrame:(CGRect)frame {
  RCTWebView *slf = [self newInitWithFrame: frame];
  
  UIWebView *webView = [slf valueForKey:@"_webView"];
  [self injectWebView: webView];
  return slf;
}

RCTWebView自身提供了一個屬性injectedJavaScript悴了,用于資源加載完畢時自動執(zhí)行的一段js腳本搏恤。比如你需要把jsBridge的js側代碼庫注入到目標頁,可以使用這個屬性湃交。

react native的JSContext獲取熟空、注入

react native項目自身使用的JSContext與WebView的JSContext不是一回事兒,也就是說你在react native的JSContext中注入接口巡揍,WebView是無法訪問到的痛阻,反之亦然。如果你需要將js接口在react中和WebView中能同時使用腮敌,必須兩邊都要注入阱当。

react native關于JSContext的封裝在RCTJSCExecutor中俏扩,它實現了一個通知RCTJavaScriptContextCreatedNotification,當JSContext創(chuàng)建完畢弊添,還未加載main.jsbundle時會發(fā)送通知录淡,JSContext作為通知的參數傳遞過來。

于是油坝,一切就簡單了嫉戚,我們注冊這個通知獲取JSContext:

+(void)load {
  
  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(recieveNotification:) name:RCTJavaScriptContextCreatedNotification object:nil];
}

+(void) recieveNotification: (NSNotification *) notification {
  
  JSContext *context = notification.object;
  __weak typeof(self) weakSelf = self;
  
  //這里由js線程調用,所以UI操作需要指定主線程
  context[@"alert"] = ^(NSString *message) {
    
    dispatch_async(dispatch_get_main_queue(), ^{
      
      UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"" message:@"native alert" delegate:weakSelf cancelButtonTitle:@"cancel" otherButtonTitles:nil, nil];
      
      [alert show];
    });
    
  };
}

功能實現了澈圈,不過這里要提醒一句彬檀,react native實現了一套js與native模塊化通信的機制,雖然我們依然可以給react通過JSContext注入的方式瞬女,但不建議這么使用窍帝,通過react native提供的模塊導出方法才是正道。

關于react native模塊的知識诽偷,可以參考react native之模塊

如果需要理解react native通信機制原理坤学,可以參考react native之OC與js之間交互

這些都偏源碼,可能有的讀者不喜歡看报慕,后面會單獨出一篇react native模塊開發(fā)的章節(jié)深浮。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市眠冈,隨后出現的幾起案子飞苇,更是在濱河造成了極大的恐慌,老刑警劉巖洋闽,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件玄柠,死亡現場離奇詭異,居然都是意外死亡诫舅,警方通過查閱死者的電腦和手機羽利,發(fā)現死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來刊懈,“玉大人这弧,你說我怎么就攤上這事⌒檠矗” “怎么了匾浪?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長卷哩。 經常有香客問我蛋辈,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任冷溶,我火速辦了婚禮渐白,結果婚禮上,老公的妹妹穿的比我還像新娘逞频。我一直安慰自己纯衍,他們只是感情好,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布苗胀。 她就那樣靜靜地躺著襟诸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪基协。 梳的紋絲不亂的頭發(fā)上歌亲,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天,我揣著相機與錄音澜驮,去河邊找鬼应结。 笑死,一個胖子當著我的面吹牛泉唁,可吹牛的內容都是我干的。 我是一名探鬼主播揩慕,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼亭畜,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了迎卤?” 一聲冷哼從身側響起拴鸵,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蜗搔,沒想到半個月后劲藐,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡樟凄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年聘芜,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缝龄。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡汰现,死狀恐怖,靈堂內的尸體忽然破棺而出叔壤,到底是詐尸還是另有隱情瞎饲,我是刑警寧澤,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布炼绘,位于F島的核電站嗅战,受9級特大地震影響,放射性物質發(fā)生泄漏俺亮。R本人自食惡果不足惜驮捍,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一疟呐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧厌漂,春花似錦萨醒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至旨椒,卻和暖如春晓褪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背综慎。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工涣仿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人示惊。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓好港,卻偏偏與公主長得像,于是被迫代替她去往敵國和親米罚。 傳聞我的和親對象是個殘疾皇子钧汹,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內容