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

UniApp WebView 与 H5 双向通信实战指南

UniApp 开发中 WebView 是连接原生与 H5 的关键。实现双向通信需处理参数传递、消息监听及结果回传。核心方案为 H5 通过 uni.webView.postMessage 发送指令,UniApp 端利用 evalJS 执行全局函数回传数据。需注意条件编译区分 App 与 H5 逻辑,App 端获取 webview 实例需延时,Token 传递建议拼接 URL。调试时注意本地 IP 配置及权限声明,确保跨端交互稳定可靠。

极客零度发布于 2026/4/12更新于 2026/5/2515 浏览
UniApp WebView 与 H5 双向通信实战指南

UniApp WebView 与 H5 双向通信实战指南

在 UniApp 开发里,WebView 是连接原生应用和 H5 页面的桥梁。特别是需要复用现有 Web 页面或集成复杂交互时,这块能力必不可少。今天结合实际案例,聊聊怎么实现 UniApp(App/H5)和内嵌 H5 的双向通信,顺便封装几个常用的原生调用,比如扫码、打电话、蓝牙打印这些。

核心需求与技术背景

通常我们需要在 UniApp 里嵌入 H5,并搞定这几件事:

  1. H5 触发原生能力(扫码、拨号、蓝牙、上传)。
  2. 原生处理完把结果回传给 H5。
  3. 兼容 App 和 H5 端的逻辑差异。
  4. 给 H5 透传鉴权 Token 等参数。

整体思路

参数传递上,UniApp 打开 WebView 时直接拼接到 URL 里传 Token。 H5 到 UniApp 这边,用 uni.webView.postMessage 发指令,UniApp 监听 message 事件接收。 UniApp 到 H5 这边,通过 evalJS 执行 H5 的全局函数来回传结果。 至于端兼容,靠条件编译区分 App 和 H5 的处理逻辑。

代码实现详解

UniApp 端:WebView 容器

这里有个坑要注意,App 端获取 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: {
    initAppWebView() {
      uni.showLoading({ title: '加载中' });
      var currentWebview = this.$scope.$getAppWebview();
      setTimeout(() => {
        wv = currentWebview.children()[0];
        uni.hideLoading();
      }, 1000);
    },
    initH5Listener() {
      window.addEventListener('message', (e) => {
        console.log('接收 H5 消息', e.data);
      }, false);
    },
    addQueryParam(url, param) {
      return url.includes('?') ? `${url}&${param}` : `${url}?${param}`;
    },
    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);
      }
    },
    submit() {
      const data = { name: 'wft', timestamp: Date.now() };
      wv.evalJS(`window.msgFromUniApp(${JSON.stringify(data)})`);
    }
  }
};
</script>

H5 端:页面结构与通信

H5 侧需要引入桥接文件,并准备好接收回调的全局函数。

<!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);
      });
    });

    window.msgFromUniApp = function (val) {};
    window.scanCodeFromUniApp = function (val) {};
    window.chooseImageFromUniApp = function (val) {};
    window.chooseVideoFromUniApp = function (val) {};
  </script>
</body>
</html>

业务页面里,我们重写这些全局回调来更新 UI。

<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() {
    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,
          sizeTypeIndex: 2,
          url: 'http://your-server/upload',
          bucketName: 'jingsheng'
        }
      });
    },
    handleUploadVideo() {
      this.postMessage('chooseVideo', {
        options: {
          maxDuration: 60,
          sourceTypeIndex: 3,
          url: 'http://your-server/upload',
          bucketName: 'jingsheng'
        }
      });
    }
  }
};
</script>

避坑指南

实际踩坑的时候,这几个点得留神:

  1. 本地调试:H5 用 Live Server 打开时,地址换成局域网 IP(如 192.168.x.x),别用 127.0.0.1,手机扫不到。
  2. 实例获取:App 端拿 webview 实例必须延时 1 秒左右,不然可能拿到空的。
  3. 数据序列化:evalJS 传复杂对象记得 JSON.stringify,不然容易语法报错。
  4. 端兼容:App 用 evalJS,H5 用 window.postMessage,条件编译别搞混了。
  5. 权限配置:manifest.json 里要把扫码、电话等权限配好,不然会闪退。
  6. 版本要求:App 端使用的 uni.webview.js 版本需匹配,建议查阅官方最新文档确认兼容性。

总结

UniApp 和 H5 双向通信的核心在于:H5 发 postMessage,UniApp 收消息后 evalJS 回传。 一定要做好条件编译,App 依赖 webview 实例,H5 依赖 window 对象。 细节决定成败,IP 替换、参数序列化、权限配置这些都得到位,跨端通信才能稳。

这套方案下来,既能复用 Web 成果,又能发挥原生优势,开发体验比较顺畅。

目录

  1. UniApp WebView 与 H5 双向通信实战指南
  2. 核心需求与技术背景
  3. 整体思路
  4. 代码实现详解
  5. UniApp 端:WebView 容器
  6. H5 端:页面结构与通信
  7. 避坑指南
  8. 总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • 基于 ResNet50 与 TensorFlow 的宠物识别系统
  • 大型语言模型(LLM)核心技术与应用学习路线
  • 链表经典算法题解:相加、重排与合并
  • VSCode 本地部署 DeepSeek 模型实战指南
  • Python 标准库详解:200+ 常用模块分类整理与使用指南
  • 使用 C++ 调用通义万相 2.1 进行高效 AI 视频生成
  • Trae AI 编辑器核心功能与实战指南
  • 开源教程:动手学大模型应用开发
  • Z 字形变换与外观数列算法解析
  • Python 面向对象编程基础:类、对象与核心概念详解
  • RS485 收发器在 FPGA 中的应用及毛刺处理
  • Z 字形变换与外观数列算法实战解析
  • ns-3 虚拟现实突发流量框架实现与建模分析
  • 基于强化学习的多无人机对抗决策生成与优化方法研究
  • 算法实战:Z 字形变换与外观数列解析
  • Vivado 基础使用指南:D 触发器设计与烧录
  • C++ Windows 开发 UTF-8 与 GBK 编码混乱问题及解决
  • 程序员职业现状分析与稳妥兼职指南
  • C++ 关联式容器详解:set、map 及其变体
  • Linux 进程间通信进阶:消息队列与信号量

相关免费在线工具

  • 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