跨平台应用开发进阶(十七) :uni-app实现内嵌H5应用

SHQ5785 发表于 2022/05/28 08:59:36 2022/05/28
【摘要】 一、前言uni-app应用开发过程中,需要应用内嵌H5页面。现如今,各大APP平台都有属于自己的小程序体系,各种各样的应用都可直接内嵌在APP中实现一站式体验。使用uniapp开发的APP如何实现这样的功能呢?答案就是内嵌web-view。 二、 码上谈兵📢 注意事项:APP中有vue页面及nvue页面,两种页面均可内嵌web-view,但两种页面的表现不一:每个vue页面,其实都是一个...

一、前言

uni-app应用开发过程中,需要应用内嵌H5页面。

现如今,各大APP平台都有属于自己的小程序体系,各种各样的应用都可直接内嵌在APP中实现一站式体验。使用uniapp开发的APP如何实现这样的功能呢?答案就是内嵌web-view
在这里插入图片描述

二、 码上谈兵

📢 注意事项:
APP中有vue页面及nvue页面,两种页面均可内嵌web-view,但两种页面的表现不一:

  • 每个vue页面,其实都是一个webview,而vue页面里的web-view组件,其实是webview里的一个子webview。这个子webview被append到父webview上。
  • vue页面会自动铺满整个页面,接收web-view页面通信使用的是@message
  • nvue页面则需要指定页面宽高,接收web-view页面通信使用的是@onPostMessage
  • app-vueweb-view组件不支持自定义样式,而v-show的本质是改变组件的样式。即组件支持v-if而不是支持v-show
  • <web-view> 组件默认铺满全屏并且层级高于前端组件。App端想调节大小或在其上覆盖内容需使用plus规范。
  • H5端的web-view其实是被转为iframe运行,使用的是当前的浏览器;
  • App端,iOS,是分为UIWebviewWKWebview的,2.2.5+起默认为WKWebview
  • nvue web-view 必须指定样式宽高;
  • App 网页向应用 postMessage 为实时消息;
  • app-nvue web-view 默认没有大小,可以通过样式设置大小,如果想充满整个窗口,设置 flex: 1 即可,标题栏不会自动显示 web-view 页面中的 title。如果想充满整个窗口且想要显示标题推荐使用 vue 页面的 web-view(默认充满屏幕不可控制大小), 想自定义 web-view 大小使用 nvue web-view

APP通知web-view页面,无论是vue页面还是nvue页面,只有evalJS方法,但调用姿势不一致。

vue页面调用:

<template>
	<web-view :src="url" @message="message" :webview-styles="webviewStyles" @onPostMessage="handlePostMessage"></web-view>
	<button class="button" @click="evalJs">evalJs(改变webview背景颜色)</button>
</template>
...
<script>
export default {
	data() {
		return {
			webviewStyles: {
				progress: {
					color: '#FF3333'
				}
			}
		}
	}
	onLoad() {
		this.currentWebview = this.$mp.page.$getAppWebview();
		this.currentWebview.children()[0].evalJS('xxx');
	},
	methods: {
		// webview向外部发送消息,APP接收并处理
		handlePostMessage: function(data) {
			console.log("接收到消息:" + JSON.stringify(data.detail));
		},
		// 调用 webview 内部逻辑
		evalJs: function() {
			this.$refs.webview.evalJs("document.body.style.background ='#00FF00'");
		}
	}
}
</script>

nvue页面调用:

<template>
	<web-view ref="webview" :src="url" @message="message"></web-view>
</template>
...
<script>
export default {
	xxx,
	onReady() {
		this.currentWebview = this.$refs.webview;
		this.currentWebview.evalJS('xxx');
	}
}
</script>

nvue页面中使用的web-view页面是无法调用plus API的,vue页面是可以控制外部web-view页面是否可用plus API,其他事项具体参考web-view | uni-app官网

三、适配问题剖析

Android、iOS平台顶部导航栏的表现不一:因内嵌web-view应用需要全屏显示,标题栏也交由内嵌应用自定义,故在pages.json中定义页面的时候,须将页面的titleNView设置为false。但即便如此,其表现仍然不一致,表现为Android端的页面起始位置是手机屏幕的最顶部(含状态栏在内),iOS端则是从状态栏之下开始渲染页面,为了抹平差异,可使用webviewsetStyle统一样式。

APP端设置:

export default {
	onReady() {
		this.currentWebview = this.$mp.page.$getAppWebview();
		// 提前计算好屏幕比率(screenRatio),底部安全距离(safeAreaInsetsBtm),区分平台(platform)
		let { screenRatio, safeAreaInsetsBtm, platform } = this.$store.state;
		// 由于iOS端会自动定位top到状态栏底下,故这里需要判断手机系统
		// 因调节Android端的top值能看出头部变化,故转换思路调ios端的top值
		let top = 0;
		if (platform === 'ios') {
			let info = uni.getSystemInfoSync();
			top = -info.statusBarHeight;
		}
		let bottom = safeAreaInsetsBtm / screenRatio;
		this.currentWebview.children()[0].setStyle({ top, bottom: parseInt(bottom) });
	}
}

统一两端的top至手机屏幕最顶部,bottom至手机屏幕安全距离之上。

内嵌应用中设置:

// 内嵌应用中,配置一个全局变量管理顶部样式,比如可在vuex的action中写个setPage方法
const actions = {
	setPage(context) {
		let statusbarHeight = plus.navigator.getStatusbarHeight();
		let pageStyle = { paddingTop: `calc(0.2rem + ${statusbarHeight}px)` };
		context.commit('pageStyle', pageStyle);
	}
};

内嵌应用中的数据传递:由APP中调用evalJS方法,调用到的是内嵌应用index.html中定义的方法,怎么将APP传递过来的信息传递到内嵌应用的vue页面去呢?这里推荐一个插件mitt

APP中调用:

export default {
	onReady() {
		this.currentWebview = this.$mp.page.$getAppWebview();
		this.currentWebview.children()[0].evalJS("showTip('提示测试')");
	}
}

内嵌应用的index.html中,引用下载到本地的mitt.umd.js

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="utf-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
		<meta name="renderer" content="webkit" />
		<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" />
		<!-- uni 的 SDK,必须引用。 -->
		<script src="<%= BASE_URL %>libs/uni-app/uni.app.webview.1.5.2.js"></script>
		<!--html页面与vue页面通讯js-->
		<script src="<%= BASE_URL %>libs/mitt/mitt.umd.js"></script>
		<title>xxx</title>
	</head>

	<body>
		<noscript>
			<strong>当前浏览器版本太低,请升级浏览器或使用其他浏览器</strong>
		</noscript>
		<div id="app"></div>
		<!-- built files will be auto injected -->
		<script>
			window.bus = window.mitt();
			const showTip = (tip) => {
				window.bus.emit('showTip', tip);
			};
		</script>
	</body>
</html>

内嵌应用vue页面中:

export default {
	activated() {
		// 挂载
		window.bus.on('showTip', (tip) => {
			alert(tip);
		});
	}
	deactivated() {
		// 离开时记得销毁
		window.bus.off('showTip');
	}
}

四、拓展阅读

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请发送邮件至:cloudbbs@huaweicloud.com;如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。