wz-uniapp/docs/Task1.md

12 KiB
Raw Permalink Blame History

UniApp + H5混合开发微信小程序 技术实施方案UniApp端

你希望通过UniApp开发微信小程序以web-view嵌套H5承载核心业务展示同时由UniApp原生层负责登录、退出、支付等核心能力以下是UniApp端的详细技术实施方案明确需要开发的核心模块、功能和关键逻辑。

一、整体架构与职责边界

核心分工

模块/能力 UniApp小程序端负责 H5端负责
用户身份 微信登录、token管理、用户信息存储、退出登录逻辑 接收并使用用户信息,触发退出登录指令
支付 调起微信支付、支付结果处理、支付状态同步 发起支付请求、展示支付结果
页面容器 提供tabBar框架、web-view容器、页面跳转控制 承载table栏、业务内容展示、内部页面跳转
通信 接收H5消息、主动向H5推送数据、处理通信异常 向UniApp发送指令、接收UniApp数据、展示状态反馈

二、UniApp端核心开发内容

1. 项目基础配置

1全局配置pages.json

配置tabBar、页面路由、web-view权限等核心配置

{
  "pages": [
    // 登录页
    "pages/login/login",
    // tabBar主页面嵌套web-view
    "pages/index/index",
    // 其他tabBar页面如需
    "pages/my/my"
  ],
  "tabBar": {
    "color": "#666666",
    "selectedColor": "#007AFF",
    "backgroundColor": "#ffffff",
    "borderStyle": "black",
    "list": [
      {
        "pagePath": "pages/index/index",
        "text": "首页",
        "iconPath": "static/tabbar/home.png",
        "selectedIconPath": "static/tabbar/home-active.png"
      },
      {
        "pagePath": "pages/my/my",
        "text": "我的",
        "iconPath": "static/tabbar/my.png",
        "selectedIconPath": "static/tabbar/my-active.png"
      }
    ]
  },
  "permission": {
    "scope.userLocation": {
      "desc": "你的位置信息将用于小程序定位"
    }
  },
  "webview": {
    "webviewParameter": {
      "allowUniversalAccessFromFileURLs": true,
      "allowFileAccess": true
    }
  }
}

2微信小程序配置manifest.json

开启微信小程序相关权限配置AppID等

{
  "mp-weixin": {
    "appid": "你的微信小程序AppID",
    "setting": {
      "urlCheck": false, // 允许访问非域名白名单的H5开发阶段正式需配置白名单
      "es6": true,
      "postcss": true
    },
    "usingComponents": true,
    "permission": {
      "scope.userInfo": {
        "desc": "获取你的用户信息用于登录"
      }
    }
  }
}

2. 核心页面开发

1登录页pages/login/login.vue

负责微信授权登录、获取用户信息、token存储

<template>
  <view class="login-container">
    <button open-type="getUserProfile" @click="wxLogin" type="primary">微信快捷登录</button>
  </view>
</template>

<script>
export default {
  methods: {
    async wxLogin() {
      try {
        // 1. 获取微信登录code
        const loginRes = await uni.login({ provider: 'weixin' });
        if (loginRes.code) {
          // 2. 调用后端接口换取用户token和信息
          const userRes = await uni.request({
            url: '你的后端登录接口', // 如https://api.xxx.com/wx/login
            method: 'POST',
            data: {
              code: loginRes.code,
              appid: '你的小程序AppID'
            }
          });

          if (userRes.data.code === 200) {
            // 3. 存储用户信息和token全局可用
            const { token, userInfo } = userRes.data.data;
            uni.setStorageSync('wx_token', token);
            uni.setStorageSync('user_info', userInfo);
            
            // 4. 登录成功跳转到首页
            uni.switchTab({ url: '/pages/index/index' });
            uni.showToast({ title: '登录成功', icon: 'success' });
          } else {
            uni.showToast({ title: userRes.data.msg, icon: 'none' });
          }
        }
      } catch (err) {
        uni.showToast({ title: '登录失败,请重试', icon: 'none' });
        console.error('登录异常:', err);
      }
    }
  }
};
</script>

<style scoped>
.login-container {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
}
button {
  width: 80%;
  height: 80rpx;
  font-size: 32rpx;
}
</style>

2tabBar首页pages/index/index.vue

核心容器页嵌套web-view、处理与H5的通信

<template>
  <view class="webview-container">
    <!-- web-view组件嵌套H5页面绑定通信事件 -->
    <web-view 
      :src="h5Url" 
      @message="handleH5Message"
      id="main-webview"
    ></web-view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      h5Url: '你的H5网页地址', // 如https://h5.xxx.com/home
      userToken: '',
      userInfo: {}
    };
  },
  onShow() {
    // 页面显示时校验登录态,无登录态则跳转登录页
    this.checkLoginStatus();
    // 拼接用户信息到H5链接初始化传参
    this.initH5Url();
  },
  methods: {
    // 1. 校验登录状态
    checkLoginStatus() {
      this.userToken = uni.getStorageSync('wx_token');
      this.userInfo = uni.getStorageSync('user_info');
      if (!this.userToken) {
        uni.redirectTo({ url: '/pages/login/login' });
      }
    },

    // 2. 初始化H5链接携带用户信息
    initH5Url() {
      if (this.userToken) {
        // 拼接token、用户ID等关键信息到H5链接
        this.h5Url = `${this.h5Url}?token=${this.userToken}&userId=${this.userInfo.id}&nickName=${encodeURIComponent(this.userInfo.nickName)}`;
      }
    },

    // 3. 接收H5发送的消息核心通信逻辑
    handleH5Message(e) {
      // H5发送的消息格式建议{ type: '指令类型', data: '指令参数' }
      const [message] = e.detail.data;
      if (!message) return;

      switch (message.type) {
        // H5触发支付
        case 'triggerPay':
          this.handlePay(message.data);
          break;
        // H5触发退出登录
        case 'triggerLogout':
          this.handleLogout();
          break;
        // H5请求重新获取用户信息
        case 'getUserInfo':
          this.sendUserInfoToH5();
          break;
        default:
          console.log('未知指令类型:', message.type);
      }
    },

    // 4. 处理支付逻辑UniApp原生调起微信支付
    async handlePay(payParams) {
      try {
        // 4.1 调用后端获取微信支付参数如prepay_id
        const payRes = await uni.request({
          url: '你的后端支付接口', // 如https://api.xxx.com/pay/create
          method: 'POST',
          data: {
            token: this.userToken,
            ...payParams // H5传递的订单ID、金额等参数
          }
        });

        if (payRes.data.code !== 200) {
          uni.showToast({ title: payRes.data.msg, icon: 'none' });
          // 通知H5支付失败
          this.sendMsgToH5({ type: 'payFail', data: payRes.data.msg });
          return;
        }

        // 4.2 调起微信支付
        const wxPayParams = payRes.data.data;
        uni.requestPayment({
          timeStamp: wxPayParams.timeStamp,
          nonceStr: wxPayParams.nonceStr,
          package: wxPayParams.package,
          signType: 'MD5',
          paySign: wxPayParams.paySign,
          // 支付成功回调
          success: () => {
            uni.showToast({ title: '支付成功', icon: 'success' });
            this.sendMsgToH5({ type: 'paySuccess', data: '支付成功' });
          },
          // 支付失败/取消回调
          fail: (err) => {
            uni.showToast({ title: '支付失败', icon: 'none' });
            this.sendMsgToH5({ type: 'payFail', data: err.errMsg });
          }
        });
      } catch (err) {
        console.error('支付异常:', err);
        this.sendMsgToH5({ type: 'payFail', data: '支付接口异常' });
      }
    },

    // 5. 处理退出登录
    handleLogout() {
      // 清除本地存储的登录态
      uni.removeStorageSync('wx_token');
      uni.removeStorageSync('user_info');
      // 通知H5退出成功
      this.sendMsgToH5({ type: 'logoutSuccess' });
      // 跳转登录页
      uni.redirectTo({ url: '/pages/login/login' });
    },

    // 6. 主动向H5发送消息
    sendMsgToH5(msg) {
      // 创建web-view上下文指定ID与页面中web-view的id一致
      const webViewCtx = uni.createWebViewContext('main-webview');
      // 发送消息H5需监听message事件接收
      webViewCtx.postMessage({ data: msg });
    },

    // 7. 主动向H5推送用户信息
    sendUserInfoToH5() {
      this.sendMsgToH5({
        type: 'userInfo',
        data: {
          token: this.userToken,
          userId: this.userInfo.id,
          nickName: this.userInfo.nickName,
          avatarUrl: this.userInfo.avatarUrl
        }
      });
    }
  }
};
</script>

<style scoped>
.webview-container {
  width: 100%;
  height: 100vh;
}
</style>

3“我的”页面pages/my/my.vue

可选,如需原生展示用户信息、退出按钮等:

<template>
  <view class="my-container">
    <view class="user-info">
      <image :src="userInfo.avatarUrl" class="avatar"></image>
      <text class="nickname">{{ userInfo.nickName }}</text>
    </view>
    <button @click="handleLogout" type="warn">退出登录</button>
  </view>
</template>

<script>
export default {
  data() {
    return {
      userInfo: {}
    };
  },
  onShow() {
    this.userInfo = uni.getStorageSync('user_info');
  },
  methods: {
    handleLogout() {
      uni.removeStorageSync('wx_token');
      uni.removeStorageSync('user_info');
      uni.redirectTo({ url: '/pages/login/login' });
    }
  }
};
</script>

<style scoped>
.my-container {
  padding: 40rpx;
}
.user-info {
  display: flex;
  align-items: center;
  margin-bottom: 60rpx;
}
.avatar {
  width: 120rpx;
  height: 120rpx;
  border-radius: 50%;
  margin-right: 30rpx;
}
.nickname {
  font-size: 36rpx;
  font-weight: bold;
}
button {
  width: 100%;
  height: 80rpx;
}
</style>

3. 全局工具类(可选)

封装通信、登录校验等通用方法,便于复用:

// common/utils.js
/**
 * 向H5发送消息
 * @param {String} webViewId web-view的ID
 * @param {Object} msg 消息内容
 */
export const sendMsgToH5 = (webViewId, msg) => {
  const webViewCtx = uni.createWebViewContext(webViewId);
  webViewCtx.postMessage({ data: msg });
};

/**
 * 校验登录态
 * @returns {Boolean} 是否已登录
 */
export const checkLogin = () => {
  const token = uni.getStorageSync('wx_token');
  if (!token) {
    uni.redirectTo({ url: '/pages/login/login' });
    return false;
  }
  return true;
};

4. 关键注意事项

  1. 域名白名单配置微信小程序后台需配置H5域名、后端接口域名的白名单否则无法访问
  2. 通信格式统一UniApp与H5的通信消息需约定固定格式{type: 'xxx', data: 'xxx'}),避免解析异常;
  3. 支付权限:确保小程序已开通微信支付权限,后端支付接口需正确对接微信支付商户平台;
  4. 登录态同步H5页面刷新时需重新从UniApp获取登录态可通过URL参数或postMessage
  5. 兼容性web-view组件在微信小程序中部分JS API有限制需避免在H5中调用小程序专属API如支付、登录

总结

UniApp端核心开发内容可归纳为3个关键点

  1. 基础层配置tabBar、页面路由、微信小程序权限搭建整体框架
  2. 核心能力层开发登录页实现微信授权登录在web-view容器页处理与H5的通信封装支付、退出登录等核心逻辑
  3. 保障层:统一通信格式、配置域名白名单、处理登录态同步,确保混合开发模式稳定运行。