跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
JavaScript大前端

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

介绍 UniApp 中 WebView 与 H5 页面的双向通信方案。通过 URL 传递参数、postMessage 发送指令及 evalJS 回传结果实现交互。涵盖扫码、电话、打印、图片视频上传等功能调用。需区分 App 与 H5 端兼容逻辑,注意权限配置与调试细节,确保跨端通信稳定。

DebugKing发布于 2026/4/6更新于 2026/7/2143 浏览
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 实例
export default {
  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-PLUS
    this.initAppWebView();
    // #endif
    // #ifdef H5
    this.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 拼接 Token
    addQueryParam(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)}` });
    },
    // 上传图片
    async handleChooseImage(options) {
      try {
        const imageUrl = await this.uploadImage(options);
        wv.evalJS(`window.chooseImageFromUniApp(${JSON.stringify(imageUrl)})`);
      } catch (error) {
        console.error('图片上传失败', error);
      }
    },
    // 上传视频
    async handleChooseVideo(options) {
      try {
        const videoUrl = await this.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>
export default {
  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: 'your-bucket-name'
        }
      });
    },
    handleUploadVideo() {
      this.postMessage('chooseVideo', {
        options: {
          maxDuration: 60,
          sourceTypeIndex: 3,
          url: 'http://your-server/upload',
          bucketName: 'your-bucket-name'
        }
      });
    }
  }
};
</script>

四、关键注意事项

  • 本地 H5 调试:本地运行的 H5 页面需用 Live Server 打开,且地址需替换成本机 IP(如 192.168.1.4:5500),而非 127.0.0.1,否则手机端无法访问;
  • App 端 webview 实例:必须通过 this.$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.js 文件。

五、总结

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

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

目录

  1. 一、核心需求与技术背景
  2. 二、整体实现思路
  3. 三、代码实现详解
  4. 3.1 UniApp 端:WebView 容器页面
  5. 3.2 H5 端:页面结构与通信逻辑
  6. 3.2.1 HTML 入口文件(index.html)
  7. 3.2.2 H5 业务页面(A.vue)
  8. 四、关键注意事项
  9. 五、总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 深度学习反向传播原理:链式法则与责任分配
  • 腾讯云开发 Copilot 低代码开发体验与技术分析
  • 递归、搜索与回溯算法及 FloodFill 算法详解
  • Win10 升级后频繁弹出 Copilot 窗口?彻底禁用与关闭方法
  • 用C写Web:CGI、FastCGI与Nginx模块的实战笔记
  • 6 款免费 AI 写作软件测评:辅助网文创作工具对比
  • JavaWeb 数据交换与异步请求:JSON 与 Ajax 技术详解
  • 零基础入门网络安全与 Web 安全学习路线指南
  • PostgreSQL 动态分区裁剪技术:查询性能优化解析
  • OpenClaw 智能体生态崛起及百度腾讯布局解析
  • 基于 OpenClaw 与 Claude 构建自动化写作工作流实践
  • AI 网络技术演进对路由协议的重塑分析
  • 行星减速器:结构原理、计算与 C++ 实现
  • DeepSeek 本地化部署实测:ToDesk、顺网云与海马云性能对比
  • 国产数据库新机遇:电科金仓融合技术与 AI 化演进
  • AVL 树的平衡艺术:用 C++ 实现自平衡二叉搜索树
  • Java 核心面试题汇总:JVM 并发与数据库
  • 2024 年 AI 绘画现状分析:Midjourney 与 Stable Diffusion 实用价值探讨
  • 前端新工具 OXC 性能对比:比 Prettier 快 45 倍
  • GitHub Copilot 提示词工程实战:从入门到精通

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online