在开发app的时候,有些时候需要支持跳转到外部网页,那么就要用到webview来实现。
web-view
是一个 web 浏览器组件,可以用来承载网页的容器,会自动铺满整个页面(nvue 使用需要手动指定宽高)。
最近,有个需求是跳到外部网页,外部网页使用的是react技术。
· 跳转webview
// 获取webview地址
const { webviewUrl,host } = await postWebViewDataConfigSimpleQuery()
if(!webviewUrl){
return
}
uni.navigateTo({
url:`/pages/vehicle/secondhand-car/webview-auction/webview-auction?webviewUrl=${webviewUrl}&host=${host}`
})
· webview样式设置以及设置鉴权
<template>
<view v-show="isShow">
<web-view :src="state.src" :webview-styles="state.wbStyles" @message="handlePostMessage"></web-view>
</view>
</template>
const { proxy } = getCurrentInstance()
const { windowHeight, statusBarHeight } = uni.getSystemInfoSync()
// 用户存储信息
const store = useAuthStore()
let state = reactive({
title: '聚合拍卖',
wbStyles: {
top: statusBarHeight,
height: windowHeight - statusBarHeight
},
src: '',
host: ''
})
const canBack = ref(false) // 返回键控制
const isShow = ref(false)
let wv;
onBackPress(()=> {
if (wv && canBack.value) {
wv.back();
return true;
}
return false;
})
onReady(() => {
setEvalJS()
})
onLoad(async (option) => {
uni.showLoading()
state.src = option.webviewUrl
state.host = option.host
})
/**
* 设置webview的JS信息
*/
async function setEvalJS(){
// #ifdef APP-PLUS
try {
let currentWebview = proxy.$scope.$getAppWebview(); //获取当前web-view
setTimeout(async function() {
wv = currentWebview.children()[0];
const {token,ycpSecretKey:secret} = await postYcpTokenEncrypte()
if(!token || !secret){
return
}
// const needReload = !globalThis.reloadStartTime || (globalThis.reloadStartTime && (Date.now() - globalThis.reloadStartTime > 5 * 60 * 60)) // 跳转网页的时候5小时刷新一次。因为不刷新cookie不能携带到请求头上,但是也无需每次跳转网页都刷新,有设置cookie就间隔一段时间刷新就好
const needReload = true // 调试写死
const reloadFn = needReload ? `setTimeout(function(){location.reload()})` : ``
// storeCookies是对方页面提供设置cookie的方法
const js = `storeCookies({host:'${state.host}',token:'${token}',secret:'${secret}'});${reloadFn}`
if(!globalThis.reloadStartTime){
globalThis.reloadStartTime = Date.now()
}
wv.evalJS(js)
setTimeout(function(){
isShow.value = true
uni.hideLoading()
},500)
wv.addEventListener(
'progressChanged',
function(e) {
wv.canBack
(function(e) {
canBack.value = e.canBack;
});
},
false
);
},1000); //如页面初始化调用需要写延迟
} catch (error) {
proxy.$toast(error)
}
// #endif
}
· 处理webview返回操作
// 接收到的webview消息
function handlePostMessage(event) {
try {
const data = event.detail.data || []
data.forEach((item) => {
if(item.eventName === "TopPageBackBtnClicked") {
wv.close()
setTimeout(()=>{
uni.navigateBack()
},100)
}
})
} catch (error) {
proxy.$toast(error)
}
}
· 开发中遇到的问题记录
问题1:拼写错误。注意正确拼写evalJS,而非evalJs,刚开始用evalJs一直未能执行webview页面的方法,catch也未捕获到错误代码,在使用evalJS(`alert(123)`)调试,才发现是拼写错误。
问题2:执行先后顺序问题。调用webview页面的storeCookies方法前已经执行了接口请求方法,而接口请求因为没有携带依赖cookie,所以页面弹出来’cookie为空,请重新登录’的提示。
解决方式是先隐藏webview页面,执行storeCookies方法并使用浏览器的reload方法刷新页面,再显示webview页面
问题3:App 端使用 uni.web-view.js 的最低版为 uni.webview.1.5.4.js。刚开始使用1.5.2,该版本没有uni.postMessage方法,无法发送事件给app监听。
问题4:webview页面在IOS系统手机与Android系统手势返回行为不一致。进入webview页面的二级页面,Android可以返回到首页,而iOS则返回到了app。解决方案是监听返回行为,重新处理返回。上述代码未解决该问题。