玩转 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

GLM-4.6V-Flash-WEB Web界面使用指南,拖图就出结果

GLM-4.6V-Flash-WEB Web界面使用指南,拖图就出结果 你不需要配置环境、不用写一行推理代码、甚至不用打开终端——只要把一张截图拖进浏览器窗口,几秒钟后,它就能告诉你图里写了什么、画了什么、哪里有问题。这不是未来预告,而是你现在就能在本地跑起来的真实体验。 GLM-4.6V-Flash-WEB 是智谱AI最新开源的轻量级视觉语言模型,专为Web端实时交互而生。它不像某些“实验室模型”那样只存在于论文和Benchmark表格里,而是真正做到了:部署快、启动快、响应快、上手更快。一块RTX 3090,一个浏览器,一次拖拽,结果即刻呈现。 本文不讲训练原理,不列参数表格,不堆技术术语。我们只聚焦一件事:怎么用好它的Web界面?从零开始,到稳定产出,每一步都清晰可操作。 1. 为什么说“拖图就出结果”不是宣传话术? 很多多模态模型标榜“支持图文理解”,但实际用起来才发现:要装依赖、改路径、调精度、修CUDA版本、

前端防范 XSS(跨站脚本攻击)

目录 一、防范措施 1.layui util  核心转义的特殊字符 示例 2.js-xss.js库 安装 1. Node.js 环境(npm/yarn) 2. 浏览器环境 核心 API 基础使用 1. 基础过滤(默认规则) 2. 自定义过滤规则 (1)允许特定标签 (2)允许特定属性 (3)自定义标签处理 (4)自定义属性处理 (5)转义特定字符 常见场景示例 1. 过滤用户输入的评论内容 2. 允许特定富文本标签(如富文本编辑器内容) 注意事项 更多配置 XSS(跨站脚本攻击)是一种常见的网络攻击手段,它允许攻击者将恶意脚本注入到其他用户的浏览器中。

详细教程:如何从前端查看调用接口、传参及返回结果(附带图片案例)

详细教程:如何从前端查看调用接口、传参及返回结果(附带图片案例)

目录 1. 打开浏览器开发者工具 2. 使用 Network 面板 3. 查看具体的API请求 a. Headers b. Payload c. Response d. Preview e. Timing 4. 实际操作步骤 5. 常见问题及解决方法 a. 无法看到API请求 b. 请求失败 c. 跨域问题(CORS) 作为一名后端工程师,理解前端如何调用接口、传递参数以及接收返回值是非常重要的。下面将详细介绍如何通过浏览器开发者工具(F12)查看和分析这些信息,并附带图片案例帮助你更好地理解。 1. 打开浏览器开发者工具 按下 F12 或右键点击页面选择“检查”可以打开浏览器的开发者工具。常用的浏览器如Chrome、Firefox等都内置了开发者工具。下面是我选择我的一篇文章,打开开发者工具进行演示。 2. 使用

Cursor+Codex隐藏技巧:用截图秒修前端Bug的保姆级教程(React/Chakra UI案例)

Cursor+Codex隐藏技巧:用截图秒修前端Bug的保姆级教程(React/Chakra UI案例) 前端开发中最令人头疼的莫过于那些难以定位的UI问题——元素错位、样式冲突、响应式失效...传统调试方式往往需要反复修改代码、刷新页面、检查元素。现在,通过Cursor编辑器集成的Codex功能,你可以直接用截图交互快速定位和修复这些问题。本文将带你从零开始,掌握这套革命性的调试工作流。 1. 环境准备与基础配置 在开始之前,确保你已经具备以下环境: * Cursor编辑器最新版(v2.5+) * Node.js 18.x及以上版本 * React 18项目(本文以Chakra UI 2.x为例) 首先在Cursor中安装Codex插件: 1. 点击左侧扩展图标 2. 搜索"Codex"并安装 3. 登录你的OpenAI账户(需要ChatGPT Plus订阅) 关键配置项: // 在项目根目录创建.