React native中嵌入H5---学习记录目前公司的 RN 项目规模较大,然而,RN 环境的安装流程颇为复杂,学习
需求:
目前公司的 RN 项目规模较大,然而,RN 环境的安装流程颇为复杂,学习成本曲线较高。要参与该项目,人员必须具备一定的 RN 基础,这一硬性要求致使 RN 功能重构的任务被迫搁置。相较而言,掌握 React 的人员数量相对更多,对 React 的熟练程度也更高。基于此,在针对 APP 功能进行重构时,公司计划采用 H5 嵌入的方式,以实现参与人员的自由化,提升整体的参与度与开发进程!
RN和H5的优缺点:
rn的优点
- **性能较好:能够提供接近原生应用的性能体验。**例如,一些对性能要求较高的移动游戏,使用 RN 开发能够保证流畅的运行效果。
- **代码复用性高:可以在不同的移动平台(如 iOS 和 Android)上复用大部分代码。**像一些跨平台的办公应用,一次开发就能在多个平台发布,节省了开发成本和时间。
- **开发效率较高:利用 JavaScript 和 React 框架,开发者可以更快速地构建应用。**很多新兴的创业公司,借助 RN 快速推出产品的原型进行市场验证。
rn的缺点
- 学习曲线较陡峭:需要开发者同时掌握 JavaScript、React 以及原生移动开发的知识。对于新手开发者来说,可能需要花费较多的时间和精力去学习和适应。
- 调试复杂:在调试过程中可能会遇到一些难以排查的问题。
H5的优点
- **开发成本低:无需针对不同平台进行单独开发。**比如简单的宣传页面,用 H5 开发能快速上线。
- **传播方便:可以通过链接轻松分享和访问。**常见的活动推广页面,通过社交媒体分享就能快速传播。
- **更新及时:无需经过应用商店的审核流程,可随时更新内容。**对于一些时效性强的信息展示,如新闻资讯,能及时推送最新内容。
H5的缺点
- **性能相对较差:尤其是在处理复杂交互和大量数据时。**比如复杂的电商购物流程,可能会出现卡顿。
- **功能受限:某些硬件相关的功能(如摄像头、蓝牙等)调用不如原生应用方便。**像一些需要高精度定位的应用,H5 实现起来较为困难。
方法:
使用webview在RN中嵌入H5以及实现RN与webview之间的通信
使用:
1.安装并引用webview:
import WebView, { WebViewMessageEvent } from 'react-native-webview'
2.在RN中使用Webview,(这里是写了一个WebviewPage组件,做了一些封装,参数随业务需要,一般用不着配置那么多)
<SafeAreaView>
<WebView
ref={webviewRef}
source={{
uri: url, //h5地址
}}
onMessage={(e) => reciveH5Message(e)} //接收h5的消息
thirdPartyCookiesEnabled={true} //在webview中启用第三方cookie
mixedContentMode="always" //WebView将允许安全源从任何其他源加载内容
cacheEnabled={false} //webvie是否使用浏览器缓存
overScrollMode="never" //滚动模式
bounces={false} //网页视图在到达内容边缘时是否反弹
showsHorizontalScrollIndicator={false} //是否展示水平滚动条
showsVerticalScrollIndicator={false} //是否展示垂直滚动条
automaticallyAdjustContentInsets={false} //控制是否为放置在导航栏、标签栏或工具栏后面的网页视图调整内容插入
startInLoadingState={true} //强制WebView在首次加载时显示加载视图
injectedJavaScript={`(() => {
localStorage.setItem('access_token', '${JSON.stringify(authModel?.state?.userInfos?.access_token)}');
localStorage.setItem('userInfos', '${JSON.stringify(authModel?.state?.userInfos)}');
localStorage.setItem('isApp', 'true');
true;
})();`} //在webview加载完页面后执行的js(传入的是字符串)
onContentProcessDidTerminate={(syntheticEvent) => {
webviewRef.current?.reload();
}} //当WebView内容进程终止时调用
onLoadProgress={e => {
setCurrentH5(e?.nativeEvent)
}} //webview加载时执行
renderLoading={() => (
<View style={styles.container}>
<FastImage style={cs.loadingBox} source={require('@xlb/common/src/assets/gif/loading.gif')} resizeMode={FastImage.resizeMode.contain} />
</View>
)} //返回加载函数
renderError={() => (
<View>
<Text>加载失败,请返回后重新打开</Text>
</View>
)} //加载失败返回函数
/>
</SafeAreaView>
3.使用WebViewPage组件:
<WebViewPage
url={routes.dateCheck}
onMessage={reciveH5Message} //当前业务功能接收到H5消息时做处理
ref={webviewRef}> //每个webview都有单独的ref
</WebViewPage>
4.H5(Webview)中发送消息给RN(这里写了一个需要用到的功能对象):
const NativeBridge = {
postMessage: (method: string, data?: any) => {
let params = {
method: method,
...(data && {
data: data,
}),
};
window.ReactNativeWebView?.postMessage(JSON.stringify(params)); //发送消息给RN
},
/**
* 跳转到登录页面
*/
logout: () => {
NativeBridge.postMessage('logout');
},
/**
* 返回原生上一页
*/
goBack: () => {
NativeBridge.postMessage('backRN');
},
/**
* 选择门店
*/
selectStore: (data: any) => {
NativeBridge.postMessage('selectStore', data);
},
...
};
export default NativeBridge;
5.RN接收到消息(按照获取到的请求资源方法做相对应的处理):
const reciveH5Message = async (e: any) => {
let params = JSON.parse(e.nativeEvent.data)
if (params.method === 'selectStores') {
//TODO.....
changeStore()
} else if (params.method === 'changeOrientation') {
//TODO.....
changeOrientation()
}else if (params.method === 'backRN') {
//退出当前网页
navigation.goBack()
}
...
}
6.RN发消息给H5:
const sendMessageToH5 = (method: any, result?: any) => {
let data = {
method: method,
data: result,
}
webviewRef.current?.postMessage(JSON.stringify(data))
}
7.H5收到消息后存储数据(这里写了一个通用Hook)
const useJsb = (onHandleMessage: (method: string, result: object) => {}) => {
const [jsbData, setJsbData] = useState({});
// JSB通信
const handleMessage = (event: any) => {
const data = event.data;
if (typeof data === 'string') {
console.log('收到消息了', event.data, jsbData);
try {
const result = JSON.parse(data);
const method = result.method;
if (method === 'selectStoreMultiple') { //监听到的消息类别做处理
console.log('收到多选门店消息了', result?.data);
setJsbData({ //塞数据
...jsbData,
multipleStore: result?.data,
});
}else if .....
} else {
// 自己处理 msg
onHandleMessage && onHandleMessage(method, result);
}
} catch (e) {
console.error('消息数据解析错误');
}
}
};
useEffect(() => {
window.addEventListener('message', handleMessage, true); //监听message
return () => {
window.removeEventListener('message', handleMessage, true);
};
}, []);
return [jsbData, setJsbData];
};
export default useJsb;
转载自:https://juejin.cn/post/7397025359532703744