小程序webview调试

背景

由于项目历史原因,接手的小程序代码是原生写的,微信一套,支付宝一套,导致后续迭代的时候微信上的同样的功能要做俩遍,兼容支付宝,很是麻烦。后来讨论决定新增的部分功能,复杂度不高的,优先使用 webview 实现,小程序里使用 webview 访问,通过注入 token 交互.

前置条件

  1. 小程序 webview 访问仅支持 https

  2. webview 地址仅支持域名,不支持 IP 和端口

步骤

  1. 微信公众平台进入公众号设置的功能设置里填写 JS 接口安全域名

  2. 前端静态资源服务器上传微信校验文件并通过验证

  3. 前端引入 jweixin ,并初始化需要申请的接口列表

  • index.html 引入 jweixin

  • onload : 首次进入将 url 存储用于 jWeixin 验签

  • 初始化,申请接口列表

::: code-group

<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
console.log(location.href.split('#')[0], '-------------------------pageOnloadInit执行----------------------')
storage.set(storage.webviewSignUrl, location.href.split('#')[0])
initJssdk (util, api, data) {
  return new Promise((resolve, reject) => {
    const apiSrc = api.wxminiappWxAppGetSign
    const params = {
      url: data.link
    }
    uni.showLoading()
    util.postData.call(this, apiSrc, params, 'POST', function (res) {
      if (res) {
        const data = res.data.data
        jWeixin.config({
          debug: true, // 开启调试模式
          appId: configAll.config.officialAccountAppId, // 必填,公众号的唯一标识
          timestamp: data.timestamp, // 必填,生成签名的时间戳
          nonceStr: data.nonceStr, // 必填,生成签名的随机串
          signature: data.signature, // 必填,签名
          jsApiList: [
            'openLocation', // 使用微信内置地图查看位置接口
            'getLocation' // 获取地理位置接口
          ], // 必填,需要使用的JS接口列表
          success: (res) => {
            console.log(res, 'jWeixin/success')
            resolve(res)
          },
          fail: (err) => {
            console.log(err, 'jWeixin/fail')
            reject(err)
          }
        })
        jWeixin.ready(function (res) {
          console.log(res, '-------------------------jWeixin.ready----------------------')
          // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
        })
        jWeixin.error(function (res) {
          console.log(res, '-------------------------jWeixin.error----------------------')
          // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
        })
      }
      uni.hideLoading()
    }, function (err) {
      uni.hideLoading()
      uni.showToast({ title: err.errMsg, icon: 'none', duration: 1500 })
      reject(err)
    })
  })
}

:::

注意事项

  1. 微信签名一旦调试成功,把 debug: true 注释,否则会在 ios 调试引起未知错误,其实已经验签成功了,但是开了调试页面一直报错

  2. 路由跳转不能是 tabBar ,仅支持相对路径

    jWeixin.miniProgram.redirectTo({ url: '../login/login' }, (res) => {
      console.log(res, '-------------------------navigateTo/packageA/pages/login/login----------------------')
    }, (err) => {
      console.log(err, '-------------------------navigateTo/err----------------------')
    })
  3. webview 内部没有更好的办法判断当前webview 外部环境是微信还是支付宝,采用注入runEnvwebview

    /**
     * 对应运行环境执行函数的callback
    * @param {*} e
    * @param {*} obj
    */
    const runEnvCallback = (e = 'mp-weixin', obj) => {
      const defaultEnvObj = renderEnvFormatFn()
      Object.assign(defaultEnvObj, obj)
      obj[e]()
    }
    
    /**
     * 生成所有运行环境对应的基础对象
    * @returns object
    */
    const renderEnvFormatFn = () => {
      const fn = () => { console.log('未找到对应环境回调函数') }
      return {
        app: fn,
        web: fn,
        'mp-weixin': fn,
        'mp-alipay': fn,
        'mp-baidu': fn,
        'mp-toutiao': fn
      }
    }
    
    // 使用
    util.runEnvCallback(this.runEnv,
    {
      'mp-weixin': () => {
        jWeixin.miniProgram.postMessage({ data: postData })
      }
    })
  4. 所有页面公共要执行的逻辑

    /**
     * 页面onLoad初始化参数
    */
    const pageOnloadInit = (op) => {
      return new Promise((resolve, reject) => {
        try {
          console.log(location.href.split('#')[0], '-------------------------pageOnloadInit执行----------------------')
          // 首次进入将url存储用于jWeixin验签
          storage.set(storage.webviewSignUrl, location.href.split('#')[0])
          // testUrl: http://192.168.2.181:8083/html/#/pages/index/index?token=831eb544-aa27-4869-a878-3c5eb7ac5417
          if (op.vConsole === '1') {
            const vConsole = new Vconsole()
            console.log(vConsole, '-------------------------vConsole----------------------')
          }
          if (op.token) {
            store.commit('user/SET_TOKEN', op.token)
          }
          if (op.tabType) {
            store.commit('shop/SET_TABTYPE', Number(op.tabType))
          }
          if (op.runEnv) {
            store.commit('system/SET_RUNENV', op.runEnv)
          }
          if (op.hasLocation) {
            const hasLocation = op.hasLocation === 'true'
            store.commit('location/SET_HASLOCATION', hasLocation)
          }
          if (op.longitude && op.latitude) {
            const pos = { latitude: Number(op.latitude), longitude: Number(op.longitude) }
            store.commit('location/SET_MINIPOSITION', pos)
            store.commit('location/SET_UNIPOSITION', pos)
          }
          console.log(store.getters, '-------------------------store.getters----------------------')
          resolve(true)
        } catch (err) {
          reject(err)
        }
      })
    }

参考

Last updated