混合开发 - 组件介绍
介绍
xWebviewU,原生组件插件,定制化,非官方的webview,而是原生重写,权限控制等非常细致。
平台兼容
| Harmony | andriod | IOS | UTS | UNIAPP-X SDK | version |
|---|---|---|---|---|---|
| ☑ | ☑️ | ☑ | ☑️ | 4.86+ | 1.1.20 |
文件路径
ts
@/uni_modules/tmx-ui/x-webview-u/使用
ts
<x-webview-u></x-webview-u>Props 属性
| 名称 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| height | 容器高,px,%,rpx | string | 100% |
| width | 容器宽,px,%,rpx | string | 100% |
| schema | app唤起协议白名单,在此协议内允许跳转并拉起app,tel:,mailto:,sms:等协议拉起拨号盘,发邮件,发短信 | string[] | [] |
| allowLoadUrlWhite | app在跳链接时的白名单,如果域名不在此内无法跳转,如果为空则放行所有 | string[] | [] |
| allowLoadUrl | [仅安卓支持]url连接跳转时是否允许访问返回false禁止,true放行 | (url : string | null) => boolean | (url : string | null) => true |
| addInterceptor | 执行宿主的任何方法都会先经过本函数验证返回true放行,false禁止执行 | (url : string | null,name:string) => boolean | (url : string | null,name:string) => true |
| progressChanged | 页面加载时,进度百分比回调函数 | (current:number) => void | (current:number) => void |
| showLoadProgress | 是否显示加载进度条 | boolean | true |
| loadProgressColor | 加载进度条颜色 | string | #00ca6c |
| src | 加载的url | string | https://x-ui.design |
| webConfig | 用于在所有打开的页面中注入的一个数据,任何页面可以在web中执行:tmuiApp.getConfig()得到这个数据,用于在web通过数据校验等是否允许访问或者你的其它场景所使用。 | UTSJSONObject | {} |
| callMethod | 说明见下 | (message:xWebviewUCallMethodType)=>void | (message:xWebviewUCallMethodType)=>{} |
callMethod属性说明
这是一个双向通信的属性方法。
- web执行tmuijJssdk.js中的特定方法tmuijJssdk.callMethod,会触发此方法,
- 本函数中得到的参数:message
- message.data js向宿主发送的数据
- message.success(返回值数据),在web端可以收到此方法返回的结果,tmuijJssdk.callMethod是一个异步函数会等待本方法返回结果。
- message.fail(出错信息字符串)
- 其中usccess,fail函数必须执行一个,在收到本消息后。
Events 事件
| 名称 | 参数 | 说明 |
|---|---|---|
| init | - | webview创建初始成功 |
| pageStart | (url:string|null) | 页面加载时触发 |
| pageFinish | (url:string|null) | 页面加载错误时触发 |
| pageError | (url:string|null) | 页面加载错误时触发 |
| contentheightchange | (url:number) | body高变化时触发 |
| download | (event:{url:string , userAgent:string , contentDisposition:string , mimeType:string , contentLength:number}) | 页面发起下载链接时触发 |
| postMessage | (event:{detail:string}) | web通过tmuiJssdk.postMessage向宿主app发消息时触发 |
Slots 插槽
| 名称 | 说明 | 数据 |
|---|---|---|
| - | - | - |
Ref 方法
| 名称 | 参数 | 返回值 | 说明 |
|---|---|---|---|
| navback | - | - | 后退 |
| refresh | - | - | 刷新 |
| forward | - | - | 前进 |
| clearCache | - | - | 强制清缓存并刷新页面 |
| setUserAgent | agent ?: string | - | 设置webview用户代理字符串 |
| getUserAgent | - | Promise<string> | 返回webview用户代理字符串 |
| loadUrl | url : string | - | 加载远程 url(在线模式) |
| evaJs | jsCode : string | Promise<string | null> | 宿主执行 web 全局函数或代码片段;web 调宿主见 callMethod |
| getManager | - | xMiniPrograms | 获取离线小程序管理器,见下方「离线网页(小程序模式)」 |
Ref 基础调用示例
ts
const webu = ref<ComponentPublicInstance | null>(null)
function goBack() {
webu.value?.$callMethod('navback')
}
function openRemote(url: string) {
webu.value?.$callMethod('loadUrl', url)
}
async function runJs() {
const result = await webu.value?.$callMethod('evaJs', `document.title`) as string | null
console.log(result)
}离线网页(小程序模式)
x-webview-u 内置了一套「下载 zip → 本地安装 → 离线打开」的机制,用法接近小程序:首次下载安装包,之后可断网打开本地 index.html。
适用场景:活动页、H5 业务包、需要版本热更新的混合页面等。
原理
- 通过 ref 拿到管理器:
getManager() installApp:按配置下载 zip,解压到本地沙箱,并写入配置文件checkAppInstall:检查某个appid是否已安装及版本信息openMiniProgramHostUrl:加载该appid目录下的index.html(内部走loadUrlmini)
本地根目录(插件内部):
| 平台 | 目录 |
|---|---|
| iOS / 通用 | USER_DATA_PATH/tmuiAppMiniProgram/{appid}/ |
| Android | ANDROID_INTERNAL_SANDBOX_PATH/tmuiAppMiniProgram/{appid}/ |
每个应用目录内会写入 tmuiMiniPrograms.json(安装配置),并要求根目录存在 index.html。
离线包(zip)规范
打包时请注意:
- 内容必须在 zip 根目录,不要再套一层文件夹
- 根目录必须有
index.html(入口) - 相对资源路径按根目录组织,例如
./js/app.js、./css/app.css - 可正常引入
tmuiJssdk.js,与在线模式一样和宿主通信
错误示例:
my-app.zip
└── my-app/
├── index.html
└── js/...正确示例:
my-app.zip
├── index.html
├── js/...
└── css/...获取管理器
ts
import type { xMiniPrograms } from '@/uni_modules/x-webview-u/components/x-webview-u/miniProgram.uts'
import type { xMiniProgramsInstallType } from '@/uni_modules/x-webview-u/interface.uts'
const webu = ref<ComponentPublicInstance | null>(null)
function getMiniManager(): xMiniPrograms | null {
// #ifdef APP
return webu.value?.$callMethod('getManager') as xMiniPrograms
// #endif
// #ifndef APP
return null
// #endif
}
getManager()依赖已初始化的 webview 实例,请在@init之后再调用。Web 端loadUrlmini为空实现,离线模式请在 App(Android / iOS / 鸿蒙) 上使用。
管理器方法
通过 getManager() 得到的 xMiniPrograms 对象,提供以下方法:
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
checkAppInstall | id: string | Promise<xMiniProgramsInstallType | null> | 检查是否已安装;未安装返回 null,已安装返回配置信息 |
installApp | ini: xMiniProgramsInstallTypeheader: UTSJSONObject | null | Promise<boolean> | 下载并安装/更新。若已存在同 appid,会先删除再安装 |
openMiniProgramHostUrl | id: string | Promise<any | null> | 打开已安装的离线包。成功返回 true,失败返回 false |
安装配置类型 xMiniProgramsInstallType
| 字段 | 类型 | 说明 |
|---|---|---|
appid | string | 应用唯一 id,自行定义,不重复即可 |
versionName | string | 版本名,如 1.1.0 |
versionNumber | number | 版本号数字,如 1100,便于比较更新 |
installUrl | string | zip 包远程下载地址 |
ts
const ini: xMiniProgramsInstallType = {
appid: 'activity-2026',
versionName: '1.0.0',
versionNumber: 1000,
installUrl: 'https://cdn.example.com/offline/activity-2026.zip'
}完整使用示例
vue
<template>
<view class="flex-1 flex flex-col">
<view class="flex flex-row flex-row-center-between pa-12">
<x-button size="small" @click="installOrUpdate">安装/更新</x-button>
<x-button size="small" @click="openOffline">打开离线包</x-button>
<x-button size="small" @click="checkInstall">检查安装</x-button>
</view>
<x-webview-u
ref="webu"
src=""
width="100%"
height="100%"
@init="onWebInit"
/>
</view>
</template>
<script setup lang="uts">
import type { xMiniPrograms } from '@/uni_modules/x-webview-u/components/x-webview-u/miniProgram.uts'
import type { xMiniProgramsInstallType } from '@/uni_modules/x-webview-u/interface.uts'
const webu = ref<ComponentPublicInstance | null>(null)
const ready = ref(false)
const packageInfo: xMiniProgramsInstallType = {
appid: 'activity-2026',
versionName: '1.0.0',
versionNumber: 1000,
installUrl: 'https://cdn.example.com/offline/activity-2026.zip'
}
function onWebInit() {
ready.value = true
}
function getMiniManager(): xMiniPrograms | null {
if (!ready.value || webu.value == null) return null
return webu.value!.$callMethod('getManager') as xMiniPrograms
}
/** 安装或强制更新(同 appid 会先删后装) */
async function installOrUpdate() {
const mgr = getMiniManager()
if (mgr == null) return
// 第二个参数可传下载 header,例如鉴权 token;无则传 null
const ok = await mgr.installApp(packageInfo, null)
uni.showToast({ title: ok ? '安装成功' : '安装失败', icon: 'none' })
}
/** 打开已安装离线包 */
async function openOffline() {
const mgr = getMiniManager()
if (mgr == null) return
const ok = await mgr.openMiniProgramHostUrl(packageInfo.appid)
if (ok != true) {
uni.showToast({ title: '未安装或缺少 index.html', icon: 'none' })
}
}
/** 检查是否已安装,并可按版本决定是否更新 */
async function checkInstall() {
const mgr = getMiniManager()
if (mgr == null) return
const info = await mgr.checkAppInstall(packageInfo.appid)
if (info == null) {
uni.showToast({ title: '未安装', icon: 'none' })
return
}
// 本地版本低于目标版本时再 installApp
if (info.versionNumber < packageInfo.versionNumber) {
await installOrUpdate()
} else {
await openOffline()
}
}
</script>推荐流程:检查 → 安装/更新 → 打开
ts
async function launchOfflineApp(ini: xMiniProgramsInstallType) {
const mgr = getMiniManager()
if (mgr == null) return
const local = await mgr.checkAppInstall(ini.appid)
const needInstall =
local == null || local.versionNumber < ini.versionNumber
if (needInstall) {
const ok = await mgr.installApp(ini, null)
if (!ok) {
console.error('离线包安装失败')
return
}
}
const opened = await mgr.openMiniProgramHostUrl(ini.appid)
if (opened != true) {
console.error('打开失败:确认 zip 根目录含 index.html')
}
}注意事项
- 仅 App 端有效:Android / iOS / 鸿蒙支持;Web 端离线加载为空实现。
- 先
@init再getManager:webview 未就绪时不要调用。 installApp会覆盖同appid:更新即重装,请保证 zip 完整可解压。- 入口文件固定为
index.html:缺失会打开失败并在控制台提示路径。 - 与
loadUrl的区别:loadUrl走在线远程地址;离线打开走openMiniProgramHostUrl→ 内部loadUrlmini。 - 鉴权下载:
installApp(ini, header)的header会传给uni.downloadFile,可带 token 等请求头。 - 离线页同样可使用
tmuiJssdk.js与宿主双向通信(postMessage/callMethod等)。
示例文件路径
见代码仓库混合开发中的 demo,apk 示例:
示例源码
uvue
vue
<template>
<view class="flex-1 ">
<x-webview-u ref="webu" src=""></x-webview-u>
</view>
</template>
<script setup>
import { useStore } from "../store"
const hostUrl = useStore.hostUrl;
const webu = ref<ComponentPublicInstance|null>(null)
const url = ref(hostUrl+'cssvar')
</script>
<style>
</style>