玩转 UniApp WebView:H5 与原生应用双向通信实战

玩转 UniApp WebView:H5 与原生应用双向通信实战

在 UniApp 开发中,WebView 是连接原生应用与 H5 页面的重要桥梁,尤其在需要复用已有 Web 页面或集成复杂 Web 交互场景时不可或缺。本文将结合实际开发案例,详细讲解如何实现 UniApp(App/H5 端)与内嵌 H5 页面的双向通信,并封装常用的原生能力调用(扫码、拨打电话、蓝牙打印、文件上传等)。

一、核心需求与技术背景

你需要在 UniApp 中通过 WebView 嵌入 H5 页面,并实现:

  1. H5 页面触发原生能力(扫码、拨打电话、蓝牙打印、图片 / 视频上传);
  2. UniApp 原生端处理完逻辑后,将结果回传给 H5 页面;
  3. 兼容 App 端和 H5 端的通信逻辑;
  4. 给 H5 页面传递鉴权 Token 等参数。

二、整体实现思路

  1. 参数传递:UniApp 在打开 WebView 时,通过 URL 拼接方式给 H5 页面传递 Token 等参数;
  2. H5 → UniApp:H5 通过uni.webView.postMessage发送指令,UniApp 监听message事件接收并处理;
  3. UniApp → H5:UniApp 通过evalJS执行 H5 页面的全局函数,将处理结果回传;
  4. 端兼容:通过 UniApp 的条件编译区分 App 端和 H5 端的通信逻辑。

三、代码实现详解

3.1 UniApp 端:WebView 容器页面

<template><view style="position: relative;"><web-view :src="reportSrc":webview-styles="webviewStyles" @message="message"></web-view></view></template><script>import{ getToken }from'@/utils/auth'import cameraTool from'../mixins/camera-tool.vue'// 上传功能混入var wv;// WebView实例exportdefault{mixins:[cameraTool],data(){return{webviewStyles:{width:'100%',height:'100%'},reportSrc:''};},onLoad(option){// 接收打开页面时传递的参数const eventChannel =this.getOpenerEventChannel(); eventChannel.on('acceptDataFromOpenerPage',(data)=>{this.reportSrc =this.addQueryParam(data.url,`token=${getToken()}`); uni.setNavigationBarTitle({title: data.title });});// #ifdef APP-PLUSthis.initAppWebView();// #endif// #ifdef H5this.initH5Listener();// #endif},methods:{// App端WebView初始化initAppWebView(){ uni.showLoading({title:'加载中'});var currentWebview =this.$scope.$getAppWebview();setTimeout(()=>{ wv = currentWebview.children()[0]; uni.hideLoading();},1000);},// H5端监听initH5Listener(){ window.addEventListener('message',(e)=>{ console.log('接收H5消息', e.data);},false);},// URL拼接TokenaddQueryParam(url, param){return url.includes('?')?`${url}&${param}`:`${url}?${param}`;},// 接收H5发来的消息(核心)message(e){const data = e.detail.data[0];const{ type, info, options }= data;const actions ={'scanCode':()=>this.handleScan(info),'phoneCall':()=>this.handlePhoneCall(info),'bluetoothPrint':()=>this.handleBluetoothPrint(info),'chooseImage':()=>this.handleChooseImage(options),'chooseVideo':()=>this.handleChooseVideo(options),'logout':()=>this.handleLogout()};if(actions[type]){ actions[type]();}else{ console.warn('未知操作类型:', type);}},// 扫一扫handleScan(){ uni.scanCode({success:(res)=>{ wv.evalJS(`window.scanCodeFromUniApp('${res.result}')`);}});},// 拨打电话handlePhoneCall(phoneNumber){ uni.makePhoneCall({phoneNumber: phoneNumber ||'114'});},// 蓝牙打印(跳转蓝牙页面)handleBluetoothPrint(printData){ uni.navigateTo({url:`/pages/common/blueTooth/index?printData=${JSON.stringify(printData)}`});},// 上传图片asynchandleChooseImage(options){try{const imageUrl =awaitthis.uploadImage(options); wv.evalJS(`window.chooseImageFromUniApp(${JSON.stringify(imageUrl)})`);}catch(error){ console.error('图片上传失败', error);}},// 上传视频asynchandleChooseVideo(options){try{const videoUrl =awaitthis.uploadVideo(options); wv.evalJS(`window.chooseVideoFromUniApp(${JSON.stringify(videoUrl)})`);}catch(error){ console.error('视频上传失败', error);}},// App主动向H5传值submit(){const data ={name:'wft',timestamp: Date.now()}; wv.evalJS(`window.msgFromUniApp(${JSON.stringify(data)})`);}}};</script>

3.2 H5 端:页面结构与通信逻辑

3.2.1 HTML 入口文件(index.html)
<!DOCTYPE html><html><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/></head><body><div id="app"></div><!-- 关键:引入 uni.webView 桥接文件 --><script src="./uni.webview.js" type="text/javascript"></script><script type="text/javascript">// 等待桥接就绪 document.addEventListener("UniAppJSBridgeReady",function(){ uni.webView.getEnv(function(res){ console.log("当前环境:", res);});});// 声明供 App 调用的全局函数 window.msgFromUniApp=function(val){}; window.scanCodeFromUniApp=function(val){}; window.chooseImageFromUniApp=function(val){}; window.chooseVideoFromUniApp=function(val){};</script></body></html>
3.2.2 H5 业务页面(A.vue)
<template><div class="hello"><h3>H5内嵌页面</h3><!-- 扫一扫 --><van-field v-model="scanCode" label="扫一扫" placeholder="扫码结果"><template #button><van-button type="primary" size="small" @click="handleScan"> 扫一扫 </van-button></template></van-field><!-- 拨打电话 --><van-field v-model="phone" label="拨打电话"><template #button><van-button type="primary" size="small" @click="makePhoneCall"> 拨打 </van-button></template></van-field><!-- 蓝牙打印 --><div class="section"><van-button type="primary" size="small" @click="handleBluetoothPrint"> 蓝牙打印 </van-button></div><!-- 上传图片 --><div class="section"><div class="preview"><img v-for="item in fileList":key="item.url":src="item.url"/></div><van-button type="primary" size="small" @click="handleUploadImage"> 上传图片 </van-button></div><!-- 上传视频 --><div class="section"><div class="preview"><video v-for="item in videoList":key="item.url":src="item.url" controls /></div><van-button type="primary" size="small" @click="handleUploadVideo"> 上传视频 </van-button></div></div></template><script>exportdefault{data(){return{scanCode:'',phone:'13800138000',fileList:[],videoList:[]};},mounted(){// 重写全局回调,接收App执行结果 window.scanCodeFromUniApp=(val)=>{this.scanCode = val;}; window.chooseImageFromUniApp=(val)=>{this.fileList =this.fileList.concat(val);}; window.chooseVideoFromUniApp=(val)=>{this.videoList =this.videoList.concat(val);}; window.msgFromUniApp=(val)=>{ console.log('App主动消息:', val);};},methods:{// 统一消息发送入口postMessage(type, payload ={}){if(!window.uni ||!window.uni.webView){ console.error('uni.webView 未就绪');return;} window.uni.webView.postMessage({data:{ type,...payload }});},handleScan(){this.postMessage('scanCode');},makePhoneCall(){this.postMessage('phoneCall',{info:this.phone });},handleBluetoothPrint(){this.postMessage('bluetoothPrint',{info:[11,22]});},handleUploadImage(){this.postMessage('chooseImage',{options:{maxNumber:2,sourceTypeIndex:3,// 1:相机 2:相册 3:两者sizeTypeIndex:2,// 1:压缩 2:原图 3:两者url:'http://your-server/upload',bucketName:'jingsheng'}});},handleUploadVideo(){this.postMessage('chooseVideo',{options:{maxDuration:60,sourceTypeIndex:3,url:'http://your-server/upload',bucketName:'jingsheng'}});}}};</script>

四、关键注意事项

  • 本地 H5 调试:本地运行的 H5 页面需用Live Server打开,且地址需替换成本机
    IP(如192.168.1.4:5500),而非127.0.0.1,否则手机端无法访问;
  • App 端 webview 实例:必须通过this. s c o p e . scope. scope.getAppWebview()获取,且需延时 1
    秒左右确保实例创建完成;
  • 参数传递格式:通过evalJS传递复杂数据时,需用JSON.stringify序列化,避免语法错误;
  • 端兼容处理:通过#ifdef APP-PLUS/#ifdef H5区分不同端的通信逻辑,H5
    端需用window.postMessage,App 端用evalJS;
  • 权限申请:调用扫码、拨打电话、文件上传等功能时,需在manifest.json中配置对应权限。
  • App 端使用 uni.web-view.js 的最低版为 uni.webview.1.5.4.js

五、总结

  • UniApp 与 H5 的双向通信核心是:H5 通过uni.webView.postMessage发消息,UniApp 通过evalJS执行 H5 全局函数回传结果;
  • 需通过条件编译区分 App 端和 H5 端的通信逻辑,App 端依赖 webview 实例,H5 端依赖 window 对象;
  • 实际开发中需注意本地调试的 IP 替换、参数序列化、权限配置等细节,确保跨端通信稳定可靠。

通过这套方案,你可以轻松实现 UniApp 原生能力与 H5 页面的深度融合,既复用了 Web 端的开发成果,又能充分利用原生应用的功能优势。

Read more

Flutter 三方库 anthropic_sdk_dart 的鸿蒙化适配指南 - 让 Claude 3.x 尖端 AI 能力深植鸿蒙应用架构

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 anthropic_sdk_dart 的鸿蒙化适配指南 - 让 Claude 3.x 尖端 AI 能力深植鸿蒙应用架构 在鸿蒙(OpenHarmony)系统的智能终端、智慧座舱或分布式办公应用中,如何高效调用顶级大模型(LLM)Claude 3.x 执行深度文本理解、视觉分析或复杂任务编排?anthropic_sdk_dart 做为官方级的 Dart 客户端,为鸿蒙开发者提供了分钟级集成 Claude 的能力。本文将带您深入实战其在鸿蒙生态中的适配细节。 前言 什么是 Anthropic SDK?它是访问 Claude 模型簇(如 Opus, Sonnet,

本地Qwen + ComfyUI 制作AI漫剧完整保姆级教程(2026年3月最新版)

本地Qwen + ComfyUI 制作AI漫剧完整保姆级教程(2026年3月最新版)

这个组合是目前零成本、无限生成、角色一致性最强的本地方案! Qwen(本地大模型)负责写剧本 + 自动拆分镜 + 生成提示词;ComfyUI负责图像生成 + 角色一致性 + 动态视频。 适合新手/日更党,低配8GB显存就能跑(推荐12GB+更流畅)。全流程免费、无网络依赖、隐私安全。 模型+:工作流:https://pan.quark.cn/s/efc430e1996d 一、硬件要求(必看) * GPU:NVIDIA 8GB显存起步(RTX 3060 12GB最稳),AMD/Intel也可(需ROCm/OneAPI)。 * 内存:16GB+ RAM(32GB最佳)。 * 硬盘:SSD 50GB+ 空闲空间。 * 系统:Windows

人工智能:大模型分布式训练与高效调参技术实战

人工智能:大模型分布式训练与高效调参技术实战

人工智能:大模型分布式训练与高效调参技术实战 1.1 本章学习目标与重点 💡 学习目标:掌握大语言模型分布式训练的核心原理、主流框架使用方法,以及高效调参策略,能够解决大模型训练过程中的算力瓶颈和效果优化问题。 💡 学习重点:理解数据并行、张量并行、流水线并行的技术差异,掌握基于DeepSpeed的分布式训练实战,学会使用超参数搜索提升模型性能。 1.2 大模型训练的核心挑战 1.2.1 单卡训练的算力瓶颈 💡 大语言模型的参数量动辄数十亿甚至上万亿,单张GPU的显存和计算能力完全无法满足训练需求。以LLaMA-2-70B模型为例: * FP32精度下,模型参数本身就需要约280GB显存,远超单张消费级或企业级GPU的显存容量。 * 训练过程中还需要存储梯度、优化器状态等数据,实际显存占用是模型参数的3-4倍。 * 单卡训练的计算速度极慢,训练一轮可能需要数月时间,完全不具备工程可行性。 1.2.2 大模型训练的核心需求 为了高效完成大模型训练,我们需要解决以下三个核心问题: 1. 显存扩容:通过并行技术,将模型参数和计算任务分布到多张GPU上,突破

AI 大模型落地系列|Eino 组件核心篇:Embedding 到底解决了什么

AI 大模型落地系列|Eino 组件核心篇:Embedding 到底解决了什么

Embedding 使用说明 * 有啥用?! * 他能干嘛? * 它不能直接干嘛? * 总结: * 浅用之法 * 食用之法 * 一、最基本用法:直接调用 `EmbedStrings` * 1. 创建 embedder * 2. 调用 `EmbedStrings` * 3. 向量拿来干嘛 * 二、完整demo * 三、带 Option 怎么用 * 四、在编排中怎么用 * 在 Chain 中使用 * 在 Graph 中使用 * 五、带 Callback 怎么用 * 六、真实场景 * 场景:做知识库问答 * 第一步:把知识库切块 * 第二步:给每个 chunk 生成向量 * 第三步:存起来