react-native WebView 리 턴 처리(비 리 턴 방법 으로 해결 가능)

12517 단어 react.nativeWebView
1.머리말
프로젝트 중 일부 페이지 의 내용 은 변경 이 비교적 빈번 한데,이 페이지 들 은 우리 가 웹 페이지 로 해결 하 는 것 을 고려 할 것 이다.
RN 프로젝트 에 공용 웹 페이지 를 제공 하고 웹 콘 텐 츠 라면 이 화면 으로 이동 해 보 여 줍 니 다.
이 때 문 제 는 웹 페이지 에 1 급 페이지,2 급 페이지 가 있 는데 이것 은 네 비게 이 션 표시 줄 리 턴 키 의 처리(그리고 안 드 로 이 드 에서 리 턴 키 의 처리)로 설계 된다 는 것 이다.
이 문 제 는 RN 홈 페이지 에서 해결 방법 을 찾 을 수 있다.현재 내 비게 이 션 상 태 를 onNavigation StateChange 라 는 리 셋 방법 으로 기록 하여 이전 페이지 로 돌아 갈 지,이 페이지 를 종료 할 지,App 의 다른 인터페이스 로 돌아 갈 지 판단 하 는 것 입 니 다.
그러나 웹 페이지 의 실현 이 React 일 때 문제 가 생 길 수 있 습 니 다.페이지 가 이동 할 때 onNavigation State Change 라 는 리 셋 방법 이 리 셋 되 지 않 았 음 을 발견 할 수 있 습 니 다!!어떻게 뚱뚱 해!!
처음에 홈 페이지 주 소 를 바 이 두 로 바 꾸 려 고 시 도 했 는데 리 셋 을 받 을 수 있 고 모든 것 이 잘 작 동 되 었 지만 우리 의 링크 로 바 꾸 면 안 되 기 때문에 냄비 를 백 스테이지 에 던 져 서 React 어느 쪽 이 잘못 쓴 줄 알 았 습 니 다.
지난 프로젝트 는 시간 이 촉박 해서 소스 코드 를 잘 볼 시간 이 없어 서 완선 하지 않 은 해결 방안 을 생각 했다.즉,웹 페이지 는 js 로 앱 을 돌려 현재 의 네 비게 이 션 상 태 를 알 리 는 것 이다.이런 해결 방식 은 우호 적 이지 않다.
지금 은 소스 코드 를 보고 나 서 야 진짜 이 유 를 알 수 있 었 다.
2.원인
다음은 이 문제 의 원인 과 나의 해결 방식 을 분석 해 보 자.
1.우선 소스 코드 의 위 치 를 찾 습 니 다.
node_modules\react-native\ReactAndroid\src\main\java\com\facebook\react\views\webview
node_modules\react-native\Libraries\Components\WebView
디 렉 터 리 구 조 는 다음 과 같 습 니 다.
 

2.실 현 된 코드 세그먼트(JAVA 엔 드)
RN 의 실제 실행 코드 는 모두 네 이 티 브 코드 이기 때문에 WebView 구성 요소 와 같은 이벤트 리 셋 은 모두 네 이 티 브 코드 의 리 셋 이 촉발 한 것 입 니 다.아래 와 같다
(ReactWebViewManager.java)rn 버 전 0.47.1

protected static class ReactWebViewClient extends WebViewClient { //WebViewClient      Android     ,             。
   protected static final String REACT_CLASS = "RCTWebView"; //        ,   JS     。

  //...

  @Override
  public void onPageStarted(WebView webView, String url, Bitmap favicon) { //       ,      
   super.onPageStarted(webView, url, favicon);
   mLastLoadFailed = false;

   dispatchEvent(
     webView,
     new TopLoadingStartEvent(   //       ,dispatch ,     js
       webView.getId(),
       createWebViewEvent(webView, url)));
  }

  //...
 }
(ReactWebViewManager.java)rn 버 전 0.43.3  ,RN 은 버 전에 따라 코드 조정 이 있 기 때문에 RN 이 업그레이드 할 때 꼼꼼 한 회귀 테스트 가 필요 하 다.

protected static class ReactWebViewClient extends WebViewClient { //WebViewClient      Android     ,             。
   protected static final String REACT_CLASS = "RCTWebView"; //        ,   JS     。

  //...

  @Override
  public void onPageStarted(WebView webView, String url, Bitmap favicon) { //       ,      
   super.onPageStarted(webView, url, favicon);
   mLastLoadFailed = false;

   dispatchEvent(
     webView,
     new TopLoadingStartEvent(   //       ,dispatch ,     js
       webView.getId(),
       createWebViewEvent(webView, url)));
  }

  @Override
  public void doUpdateVisitedHistory(WebView webView, String url, boolean isReload) { //   ,                           ,              -.-
   super.doUpdateVisitedHistory(webView, url, isReload);

   dispatchEvent(
     webView,
     new TopLoadingStartEvent(
       webView.getId(),
       createWebViewEvent(webView, url)));
  }

  //...
 }
(TopLoading StartEvent.java)JS 이벤트 리 셋

public class TopLoadingStartEvent extends Event<TopLoadingStartEvent> {
 public static final String EVENT_NAME = "topLoadingStart";  //     onLoadingStart,    RN      ,                 ,            
 private WritableMap mEventData;

 public TopLoadingStartEvent(int viewId, WritableMap eventData) {
  super(viewId);
  mEventData = eventData;
 }

 @Override
 public String getEventName() {
  return EVENT_NAME;
 }

 @Override
 public boolean canCoalesce() {
  return false;
 }

 @Override
 public short getCoalescingKey() {
  // All events for a given view can be coalesced.
  return 0;
 }

 @Override
 public void dispatch(RCTEventEmitter rctEventEmitter) {
  rctEventEmitter.receiveEvent(getViewTag(), getEventName(), mEventData);
 }
}
(node_modules\react-native\ReactAndroid\src\main\java\com\facebook\react\uimanager\UIManagerModuleConstants.java)
이 파일 에 서 는 대응 관 계 를 정의 합 니 다.

/**
 * Constants exposed to JS from {@link UIManagerModule}.
 */
/* package */ class UIManagerModuleConstants {

 /* package */ static Map getDirectEventTypeConstants() {
  return MapBuilder.builder()
    .put("topContentSizeChange", MapBuilder.of("registrationName", "onContentSizeChange"))
    .put("topLayout", MapBuilder.of("registrationName", "onLayout"))
    .put("topLoadingError", MapBuilder.of("registrationName", "onLoadingError"))
    .put("topLoadingFinish", MapBuilder.of("registrationName", "onLoadingFinish"))
    .put("topLoadingStart", MapBuilder.of("registrationName", "onLoadingStart"))
    .put("topSelectionChange", MapBuilder.of("registrationName", "onSelectionChange"))
    .put("topMessage", MapBuilder.of("registrationName", "onMessage"))
    .build();
 }
}
3.실 현 된 코드 세그먼트(JS 엔 드)
(node_modules\react-native\Libraries\Components\WebView\WebView.android.js)
아래 코드 에 서 는 onLoading Start 만 볼 수 있 습 니 다.    onLoading Finish 와 호출 됩 니 다.  updateNavigation State,문 제 는 바로 여기에 나 타 났 습 니 다.우리 의 웹 페이지 는 React 이기 때문에 한 페이지 밖 에 없습니다!그래서 한 번 만 onLoadingStart 를 호출 합 니 다.  onLoading Finish 와자세 한 페이지 를 클릭 하면 새 페이지 로 넘 어가 지 않 고 원래 페이지 를 새로 고침 합 니 다.그래서 updateNavigation State 리 셋 도 없 었 어 요.

class WebView extends React.Component {
 static propTypes = {  //            
  ...ViewPropTypes,
  renderError: PropTypes.func,
  renderLoading: PropTypes.func,
  onLoad: PropTypes.func,
  //...
  }

 render() { //      
  //...
  var webView =
   <RCTWebView
    ref={RCT_WEBVIEW_REF}
    key="webViewKey"
    style={webViewStyles}
    source={resolveAssetSource(source)}
    onLoadingStart={this.onLoadingStart}
    onLoadingFinish={this.onLoadingFinish}
    onLoadingError={this.onLoadingError}/>;

  return (
   <View style={styles.container}>
    {webView}
    {otherView}
   </View>
  );
 }

 onLoadingStart = (event) => {
  var onLoadStart = this.props.onLoadStart;
  onLoadStart && onLoadStart(event);
  this.updateNavigationState(event);
 };

 onLoadingFinish = (event) => {
  var {onLoad, onLoadEnd} = this.props;
  onLoad && onLoad(event);
  onLoadEnd && onLoadEnd(event);
  this.setState({
   viewState: WebViewState.IDLE,
  });
  this.updateNavigationState(event);
 };

 updateNavigationState = (event) => {
  if (this.props.onNavigationStateChange) {
   this.props.onNavigationStateChange(event.nativeEvent);
  }
 };
}

var RCTWebView = requireNativeComponent('RCTWebView', WebView, {  //    JAVA   ‘RCTWebView'
 nativeOnly: { messagingEnabled: PropTypes.bool, }, });


 module.exports = WebView; 
2.해결 방법
원인 을 찾 은 이상 쉽게 해결 할 수 있다
해결 방법:사용자 정의 WebView,추가 doUpdateVisited History 처 리 는 내 비게 이 션 이 변 할 때마다 JS 에 알 립 니 다.
1.다음 그림 의 파일 을 우리 프로젝트 의 Android 코드 디 렉 터 리 에 복사 합 니 다.

복사 한 Android 디 렉 터 리:

ReactWebView Manager.java 에서 몇 가지 수정 이 필요 합 니 다.

public class ReactWebViewManager extends SimpleViewManager<WebView> {
 protected static final String REACT_CLASS = "RCTWebView1"; //        

 protected static class ReactWebViewClient extends WebViewClient {
    @Override
    public void doUpdateVisitedHistory(WebView webView, String url, boolean isReload) {
      super.doUpdateVisitedHistory(webView, url, isReload);

      dispatchEvent(    //        ,dispatchEvent
          webView,
          new TopCanGoBackEvent(
              webView.getId(),
              createCanGoBackWebViewEvent(webView, url)));
    }
 }
}
TopCanGoBackEvent 는 내 가 추가 한 Event 로 내 비게 이 션 변 화 를 알 리 는 데 사 용 됩 니 다.
TopCanGoBackEvent.java

public class TopCanGoBackEvent extends Event<TopCanGoBackEvent> {

 public static final String EVENT_NAME = "topChange"; 
 private WritableMap mEventData;

 public TopCanGoBackEvent(int viewId, WritableMap eventData) {
  super(viewId);
  mEventData = eventData;
 }

 @Override
 public String getEventName() {
  return EVENT_NAME;
 }

 @Override
 public boolean canCoalesce() {
  return false;
 }

 @Override
 public short getCoalescingKey() {
  // All events for a given view can be coalesced.
  return 0;
 }

 @Override
 public void dispatch(RCTEventEmitter rctEventEmitter) {
  rctEventEmitter.receiveEvent(getViewTag(), getEventName(), mEventData);
 }
}
새 ReactWebViewPage.java

public class ReactWebViewPackage implements ReactPackage {

  @Override
  public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {

    return Collections.emptyList();
  }

  @Override
  public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
    return Arrays.<ViewManager>asList(
        new ReactWebViewManager()
    );
  }
}
그리고 MainApplication 에 이 모듈 을 추가 합 니 다.

public class MainApplication extends Application implements ReactApplication {
  @Override
  protected List<ReactPackage> getPackages() {
   return Arrays.<ReactPackage>asList(
     new MainReactPackage(),
     new ReactWebViewPackage()  //WebView
   );
  }
}
이상 은 안 드 로 이 드 가 수정 해 야 할 부분 입 니 다.ios 는 시도 해 본 적 이 없습니다.큰 차이 가 있 을 것 입 니 다.같은 이치 입 니 다.
2.다음 그림 의 파일 을 우리 프로젝트 의 JS 코드 디 렉 터 리 에 복사 하고 이름 을 수정 합 니 다.

JS 코드 디 렉 터 리:

Custom WebView.android.js 는 수정 해 야 할 부분 이 몇 군데 있 습 니 다.

/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule CustomWebView  //        
 */

var RCT_WEBVIEW_REF = 'webview1'; //        

 render() {
  var webView =
   <NativeWebView
    onLoadingStart={this.onLoadingStart}
    onLoadingFinish={this.onLoadingFinish}
    onLoadingError={this.onLoadingError}
    onChange={this.onChange} //    
   />;

  return (
   <View style={styles.container}>
    {webView}
    {otherView}
   </View>
  );
 }

 onChange = (event) => {  //    
  this.updateNavigationState(event);
 };
}

var RCTWebView = requireNativeComponent('RCTWebView1', CustomWebView, CustomWebView.extraNativeComponentConfig); //    

module.exports = CustomWebView; //    
이로써 사용자 정의 WebView 모듈 을 완성 합 니 다.웹 페이지 는 React 구현 으로 내 비게 이 션 이 불가능 한 문제 도 해결 할 수 있다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기