React Native 网页视图路由

8
我正在构建一个React-Native应用程序,其中包含一些本地屏幕和一些使用WebView加载网站的屏幕。与传统的网站导航不同,我有一个本地抽屉来切换页面。
我的问题是,该网站正在使用react-router处理URL更改,在浏览器上通过仅加载必要的代码来平稳处理URL更改。然而,当我在我的WebView中更改URL时,它会像刷新网站一样,并重新加载所有内容,导致非常缓慢的导航。
我唯一想到的“hack”是在网站上公开一个函数,以触发react-router的“go to”。
有什么想法吗?

1
我遇到了类似的问题。你有没有找到在WebView中为本地Web应用程序进行路由的解决方法? - Marc
对我来说,使用' react-router-redux '中的ConnectedRouter和' history 'v4以及'react-router' v4的旧应用程序神奇地运行。然而,当我升级堆栈并完全删除redux时,对于使用'react-router' v5的BrowserRouter的应用程序,它不再起作用。ReactNative应用程序在两种情况下都是相同的(webview和RN的确切版本)。真的很想知道如何解决新应用程序的问题... - Vitexikora
2个回答

2

经过几天的努力,我找到了以下解决方案,我承认这有点难以理解,但可以完美地运行(包括返回按钮和手势):

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中,该代码利用history对象进行导航,应用程序使用消息与此代码通信:
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>
  )

}

-1
你尝试过结合使用UseEffect路由导航吗?

请提供更多细节。在哪里实现,使用什么代码?“路由导航”具体是什么? - Vitexikora
你可以使用路由导航来切换页面,你可以在这里阅读文档 https://reactnavigation.org/docs/navigating ,并且使用 UseEffect 当有变化时可以做一些事情(例如当 true 变为 false 时,你可以执行某些操作)。 - Dicka Reynaldi
我认为你没有完全理解这个问题。这是关于使用 WebView 相互交互的两个 React 应用程序之间的交互。 - Vitexikora
这并没有回答问题。一旦您拥有足够的声望,您将能够评论任何帖子;相反,提供不需要询问者澄清的答案。- 来自审核 - hardkoded

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接