react native 如何与webview通信

前言

我们为什么会用到webview?因为我们当时的场景是需要做一个活动,为了更新活动不用每次都要更新app,所以我们决定使用H5做活动,然后把活动嵌套在RN的页面中。这样我们每次更新活动不需要更新我们的app,然后活动可以随便定制,同时也提供个性化服务。也降低开发人员的开发成本。

安装依赖、链接原生依赖项

参考文档

  1. 首先我们安装react-native-webview这个依赖包。
css 复制代码
npm install --save react-native-webview
  1. 然后需要链接原生依赖项

从 react-native 0.60 开始,自动链接将。

ios/目录中运行

复制代码
pod install

对于之前的版本的就需要手动链接。

java 复制代码
react-native link react-native-webview

为什么需要这个步骤?因为这样才可以让编译器知道将它们包含在应用程序中。

在安卓的运行环境需要注意:Android - react-native-webview 版本 <6:运行链接命令后,此模块不需要任何额外步骤

android/gradle.propertiesAndroid - react-native-webview 版本 >=6.XX:请通过编辑并添加两行代码确保您的项目中启用了 AndroidX :

ini 复制代码
android.useAndroidX=true
android.enableJetifier=true

这个原生模块链接的问题我当时也是搞了好久才搞清楚,原生的模块链接如何操作可以参考下面这两篇文章。

揭秘 React Native 模块链接第一部分:使用 Cocoapods 清理你的 iOS 设置 ☕

揭秘 React Native 模块链接第二部分:完美链接 Android 原生模块的途径⛰️

React native 与webview的H5通信

RN与H5的通信其实是通过webview作为中间通道实现的,RN发消息给webview,然后H5监听message就拿到了RN给H5的消息。

文档上有两种方式来实现

  • React Native -> Web:injectedJavaScriptprop
  • React Native -> Web:injectJavaScriptmethod

在我们的项目中我使用第一种方式,这是一个在网页首次加载后立即运行的脚本。即使页面重新加载或离开,它也只运行一次。我们只是形成一个消息通道,所以没必要动态变化的去使用injectJavaScript method这种方式。

首先我们先封装一个RN的组件,用来加载webview,并创建一个ref---webViewRef绑定在webview上。

ini 复制代码
 <View style={styles.mainWrap}>
            {h5Url ? (
                <WebView
                    style={[styles.webviewWrap]}
                    source={{uri: h5Url}}
                    renderError={renderError}
                    {...webViewProps}
                />
            ) : (
                <Text allowFontScaling={false} style={styles.errorMsg}>无效的网页地址</Text>
            )}
        </View>

webViewProps:

yaml 复制代码
 const webViewProps = useMemo(() => ({
        ref: webViewRef,
        bounces: true,
        scrollEnabled: true,
        androidHardwareAccelerationDisabled: true,
        useWebkit: true,
        startInLoadingState: true,
        domStorageEnabled: true,
        injectedJavaScript: injectedJavascriptFunc,
        onMessage: handleReceiveH5Message
    }), [handleReceiveH5Message]);

injectedJavascriptFunc:注意这里是字符串,这一步是为了给H5页面注入可以与webview通信的方法,在H5页面调用这个方法可以给webview发送消息,然后我们的RN组件就可以拿到H5传递的信息。

ini 复制代码
export const injectedJavascriptFunc = `(function() {
    window.postMessage = function(data) {
        window.ReactNativeWebView.postMessage(data);
    };
})()`;

通过webViewRef调用postMessage给webview发送消息,这里传递的消息必须是字符串。

ini 复制代码
const postMessageToH5 = useCallback(message => {
        webViewRef.current && webViewRef.current.postMessage(JSON.stringify(message));
    }, [webViewRef]);

H5如何接收到消息呢?

javascript 复制代码
function handleCallback(evt) {
  const message = evt.data; // 字符串类型
  try {
    const data = JSON.parse(message);
    if (_.isFunction(bindFunctionRef.current)) {
      bindFunctionRef.current(data);
    }
  }
  catch (e) {
    console.log('WebView message: 格式解析错误');
  }
};
​
window.addEventListener('message', handleCallback);
document.addEventListener('message', handleCallback);

这里可以看到我们注册了两遍事件,原因就是document.addEventListener在iOS上不生效。具体看这篇文章:在React Native中使用WebView的全面指南

到这一步我们就实现了从RN--->webview--->H5的通信,我们在H5的页面上就能够得到来自RN的信息了。

webview的H5与React native通信

  • Web -> React Native:postMessage方法和onMessage属性

必须设置onMessage,否则该window.ReactNativeWebView.postMessage方法将不会被注入到H5页中,这里RN就可以通过onMessage注册的方法获取到H5传递过来的消息。

那么H5如何给webview发送消息,还记得我们上面注入的injectedJavascriptFunc的代码,它是在H5页面加载后就执行的的脚本,也就是说只要H5页面加载了就执行了这段代码。

javascript 复制代码
(function() {
    window.postMessage = function(data) {
        window.ReactNativeWebView.postMessage(data);
    };
})()

那么我们相当于重写了window的postMessage方法,使其能够正确地与 React Native 的 WebView 通信。为什么这么做?

  • 原理:React Native 的 WebView 组件和 H5 页面通信时,H5 端需要通过 window.ReactNativeWebView.postMessage(data) 向 RN 发送消息。但有些 H5 页面可能只会调用标准的window.postMessage,这时消息不会被 RN 收到。
  • 作用:这段注入的 JS 代码会把 H5 里的 window.postMessage 方法重写为调用 window.ReactNativeWebView.postMessage,保证无论 H5 调用哪个方法,消息都能被 RN 层收到。
  • 使用场景:适用于 RN WebView 加载的第三方或自有 H5 页面,确保消息通信兼容性。

那么在我们的H5页面上就可以调用这个方法给webview发送消息。

javascript 复制代码
export default () => {
    // 发送消息至RN
    const postMessage = useCallback((eventName, data) => {
        console.log('发送到RN的数据为:', eventName, data);
        if (window.ReactNativeWebView) {
            const requestBody = JSON.stringify({
                type: eventName,
                data
            });
            window.ReactNativeWebView.postMessage(requestBody);
        } else {
            const requestBody = JSON.stringify({
                type: eventName,
                data
            });
            window.postMessage(requestBody);
        }
    }, []);
​
    return {
        postMessage
    };
};

然后webview通过我们注册的回调函数onMessage就得到了来自H5的信息。

vbnet 复制代码
​
    // RN接收h5的消息
    const handleReceiveH5Message = useCallback(async e => {
        const dataStr = e.nativeEvent.data;
        if (dataStr && dataStr.toString() !== 'undefined') {
            try {
            }
            catch (error) {
                throw error;
            }
        }
    }, []);
相关推荐
谜亚星3 分钟前
vue和react组件更新的一点思考
前端·前端框架
清秋8 分钟前
全网最全 ECMAScript 攻略( 更新至 ES2025)
前端·javascript·ecmascript 6
puffysang3311 分钟前
Android paging3实现本地缓存加载数据
前端
拉罐16 分钟前
React Query:彻底解决 React 数据获取难题的强大利器
前端
一涯1 小时前
用python写一个抓取股市关键词的程序
前端·python
情绪的稳定剂_精神的锚1 小时前
git提交前修改文件校验
前端
Moonbit1 小时前
MoonBit 作者寄语 2025 级清华深圳新生
前端·后端·程序员
前端的阶梯1 小时前
开发一个支持支付功能的微信小程序的注意事项,含泪送上
前端·后端·全栈
李明卫杭州1 小时前
深入理解CSS变量(Custom Properties)
前端·javascript
CoderLiu1 小时前
AI提示词工程优化指南:8个技巧,释放大语言模型的全部潜力
前端·人工智能·ai编程