【问题标题】:React Native webview routingReact Native webview 路由
【发布时间】:2018-02-05 10:48:05
【问题描述】:

我正在构建一个带有一些本机屏幕的 React-Native 应用程序,以及一些我使用 WebView 加载网站的屏幕。代替经典的网站导航,我有一个本机抽屉来切换页面。 我的问题是该网站正在使用 react-router,因此它通过仅加载必要的代码来顺利处理浏览器上的 URL 更改。尽管如此,当我更改 WebView 中的 URL 时,它就像我在刷新网站一样,它会重新加载所有内容,导致导航速度非常慢。

我认为唯一的“黑客”是在网站window 变量上公开一个函数以触发反应路由器“转到”。

有什么想法吗?

【问题讨论】:

  • 我遇到了类似的问题。你有没有找到在本地 webapps 的 WebView 中路由的解决方法?
  • 对我来说,它神奇地适用于使用来自 'react-router-redux' 的 ConnectedRouter 和来自 'history' v4 和 'react-router' v4 的 browerHistory 的旧应用程序。但是,当我升级堆栈并完全删除了 redux 时,对于带有来自 'react-router' v5 的 BrowserRouter 的应用程序,它不再工作了。 ReactNative 应用在两种情况下都是相同的(完全相同的 webview 和 RN 版本)。真的很想知道如何围绕新应用解决这个问题...

标签: javascript react-native webview react-router


【解决方案1】:

所以,经过几天的挣扎,我已经解决了以下解决方案,我承认这有点晦涩,但效果很好(包括后退按钮、手势):

React Web 应用程序中,我通过全局 Window 对象保持 ReactRouter 历史对象可用。

import { useHistory } from 'react-router'
// ...

declare global {
  interface Window {
    ReactRouterHistory: ReturnType<typeof useHistory>
  }
}

const AppContextProvider = props => {
  const history = useHistory()

  // provide history object globally for use in Mobile Application
  window.ReactRouterHistory = history

  // ...
}

React Native 移动应用程序中,我将自定义代码注入到 WebView,它利用历史对象进行导航,并且应用程序使用消息与此代码进行通信:

webViewScript.ts

// ...
export default `
  const handleMessage = (event) => {
    var message = JSON.parse(event.data)
    switch (message.type) {
      // ...
      case '${MessageTypes.NAVIGATE}':
        if (message.params.uri && message.params.uri.match('${APP_URL}') && window.ReactRouterHistory) {
          const url = message.params.uri.replace('${APP_URL}', '')
          window.ReactRouterHistory.push(url)
        }
        break
    }
  };

  !(() => {
    function sendMessage(type, params) {
      var message = JSON.stringify({type: type, params: params})
      window.ReactNativeWebView.postMessage(message)
    }

    if (!window.appListenersAdded) {
      window.appListenersAdded = true;
    
      window.addEventListener('message', handleMessage)
      
      var originalPushState = window.history.pushState
      window.history.pushState = function () {
        sendMessage('${MessageTypes.PUSH_STATE}', {state: arguments[0], title: arguments[1], url: arguments[2]})
        originalPushState.apply(this, arguments)
      }

    }
  })()
  true
`

intercom.ts(此处没有路由细节,仅用于通用通信)

import WebView, {WebViewMessageEvent} from 'react-native-webview'
import MessageTypes from './messageTypes'


export const sendMessage = (webview: WebView | null, type: MessageTypes, params: Record<string, unknown> = {}) => {
  const src = `
  window.postMessage('${JSON.stringify({type, params})}', '*')
  true // Might fail silently per documentation
  `
  if (webview) webview.injectJavaScript(src)
}

export type Message = {
  type?: MessageTypes,
  params?: Record<string, unknown>
}

export type MessageHandler = (message: Message) => void

export const handleMessage = (handlers: Partial<Record<MessageTypes, MessageHandler>>) => (
  (event: WebViewMessageEvent) => {
    const message = JSON.parse(event.nativeEvent.data) as Message
    const messageType = message.type

    if (!messageType) return
    const handler = handlers[messageType]
  
    if (handler) handler(message)
  }
)

export {default as script} from './webViewScript'

WebViewScreen.tsx

import {handleMessage, Message, script, sendMessage} from '../intercom'
// ...

const WebViewScreen = ({navigation, navigationStack}: WebViewScreenProps) => {
  const messageHandlers = {
    [MessageTypes.PUSH_STATE]: ({params}: Message) => {
      if (!params) return
      const {url} = params
      const fullUrl = `${APP_URL}${url}`
      navigationStack.push(fullUrl)
    },
    // ...
  }

  const uri = navigationStack.currentPath

  // navigation solution using history object propagated through window object
  useEffect(() => {
    if (uri) {
      sendMessage(webViewRef.current, MessageTypes.NAVIGATE, {uri})
    }
  }, [uri])

  // this is correct! Source is never going to be updated, navigation is done using native history object, see useEffect above
  // eslint-disable-next-line react-hooks/exhaustive-deps
  const source = useMemo(() => ({uri}), [])

  return (
    <View
        // ...
        refreshControl={
          <RefreshControl
              // ...
          />
        }
    >
      <WebView
          source={source}
          injectedJavaScript={script}
          onMessage={handleMessage(messageHandlers)}
          // ...
      />
    </View>
  )

}

【讨论】:

    【解决方案2】:

    你有没有尝试过 UseEffectroute navigation?

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-05-29
    • 2018-06-12
    • 2016-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多