在开发reactNative过程中,一些复杂易变的需求,我们也会经常写在H5的项目中,这时候就需要RN和H5交互的效果了,以下是我封装的webview代码
import React, { useRef, useEffect, useState } from 'react';
import Lottie from 'lottie-react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { View, SafeAreaView, Linking } from 'react-native';
import { WebView } from 'react-native-webview';
import { goBack } from '@/services/navigation';
import { navigationName, storageKey } from '@/constants';
import env from '../../../env.json';
import { Props } from './types';
import styles from './styles';
import { getCall } from '@root/src/utils';
const MyWebView: React.FC<Props> = ({ route, navigation }) => {
// 头部是否展示
const [headerShown, setHeaderShown] = useState(true);
// 获取登陆信息
const getUserInformation = async () => {
const user = await AsyncStorage.getItem(storageKey.user);
if (user) {
return user;
}
return '';
};
const webViewRef = useRef<WebView>(null);
const handlemessage = (event: any) => {
const message = JSON.parse(event.nativeEvent.data);
console.log(message);
const messageActions = {
// 返回上一页
back: goBack,
// TODO:打电话
callPhone: () => getCall(message.phone),
// 跳转H5页面保留头部
h5InterWebview: () =>
navigation.navigate({
name: navigationName.modal.webView,
key: `${navigationName.modal.webView}-${Date.now()}`,
params: {
title: message.title,
uri: `${env.H5_ENDPOINT}/${message.url}`,
color: message?.color,
},
}),
// 获取用户信息
getUserInfo: async () => {
const userInfo = await getUserInformation();
const response = {
type: 'getUserInfo',
data: JSON.parse(userInfo),
};
const responseString = JSON.stringify(response);
webViewRef?.current?.injectJavaScript(
`window.postMessage('${responseString}', '*')`,
);
},
routingJump: () => {
switch (message.stack) {
case 'home':
navigation.navigate(navigationName.tab, {
screen: navigation.home,
});
return;
case 'login':
navigation.navigate(navigationName.login);
return;
case 'my':
navigation.navigate(navigationName.my);
return;
case 'setting':
navigation.navigate(navigationName.setting.stack, {
screen: navigationName.setting.list,
});
return;
}
},
showHeader: () => {
console.log(message.show, 'message.show');
setHeaderShown(() => message.show);
},
// 添加其他操作的映射
} as void | any | boolean;
const action = messageActions[message.type];
if (action) {
action();
}
};
const callH5MethodWithParameters = async (param: any) => {
const methodName = 'userInfoCallBack';
// 使用injectJavaScript方法执行JavaScript代码,调用H5的方法并传入参数
webViewRef?.current?.injectJavaScript(`
if (window.${methodName}) {
window.${methodName}('${param}');
}
`);
};
const isAPKDownloadLink = (link: any) => {
// 检查链接的扩展名是否为.apk
if (link.endsWith('.apk')) {
return true;
}
// 检查链接中是否包含关键字,例如包含"/download"等
const keywords = ['/download', '/get-apk', '/apk-download'];
for (const keyword of keywords) {
if (link.includes(keyword)) {
return true;
}
}
return false;
};
const onShouldStartLoadWithRequest = (event: any) => {
const { url } = event;
const allowedURLs = [
'https://itunes.apple.com',
'alipays://',
'weixin://',
'alipay://',
'appmarket://',
'itms-services://',
];
if (
allowedURLs.some(allowedURL => url.startsWith(allowedURL)) ||
isAPKDownloadLink(url)
) {
Linking.canOpenURL(url).then(res => {
if (res) {
Linking.openURL(url);
}
});
return false;
}
return true; // 允许WebView
};
useEffect(() => {
navigation.setOptions({
headerShown: headerShown,
});
}, [headerShown]);
return (
<SafeAreaView
style={[{ flex: 1 }, styles.header, headerShown && { paddingTop: 0 }]}
>
<WebView
ref={webViewRef}
startInLoadingState
renderLoading={() => (
<View style={styles.loadingContainer}>
<Lottie
source={require('./loading.json')}
autoPlay
loop
style={{ width: 150, height: 150 }}
/>
</View>
)}
source={{ uri: route?.params?.uri }}
onLoadEnd={syntheticEvent => {
const { nativeEvent } = syntheticEvent;
if (
!nativeEvent.loading &&
nativeEvent.url.includes('work.weixin.qq.com/kfid')
) {
setTimeout(() => {
goBack();
}, 100); // 调
}
AsyncStorage.getItem(storageKey.user).then(res => {
if (res !== null) {
callH5MethodWithParameters(res);
}
});
const injectedJavaScript = `
window.fyhApp = {
back: function() {
window.ReactNativeWebView.postMessage(JSON.stringify({
type: 'back',
}));
},
callPhone: function(phone) {
window.ReactNativeWebView.postMessage(JSON.stringify({
type: 'callPhone',
phone,
}));
},
h5InterWebview: function(url, title, color) {
window.ReactNativeWebView.postMessage(
JSON.stringify({ type: 'h5InterWebview', url: url, title: title, color: color }),
);
},
routingJump: function(stack) {
window.ReactNativeWebView.postMessage(JSON.stringify({
type: 'routingJump',
stack,
}));
},
getUserInfo: function() {
return new Promise(function(resolve, reject) {
window.ReactNativeWebView.postMessage(JSON.stringify({ type: 'getUserInfo' }));
// 在收到消息后,通过 postMessage 回传结果
window.addEventListener('message', (event) => {
const dataFromRN = JSON.parse(event.data);
if (dataFromRN.type === 'getUserInfo') {
const userInfo = dataFromRN.data;
resolve(userInfo);
}
});
});
},
showHeader: function(show) {
window.ReactNativeWebView.postMessage(JSON.stringify({
type: 'showHeader',
show,
}));
}
};
true;
`;
if (webViewRef?.current) {
webViewRef?.current?.injectJavaScript(injectedJavaScript);
}
}}
onMessage={handlemessage}
originWhitelist={['*']}
javaScriptEnabled={true}
style={styles.container}
onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
/>
</SafeAreaView>
);
};
export default MyWebView;