原始问题

前段时间用taro做了一个移动端网页,这两天要改为小程序,发现h5特有的api用太多了,小程序版本跑不起来了,想了下就用小程序放一个webview组件放这个网页吧。
但是发现了一个严重的问题,小程序直接打开这个webview进入内页无返回按钮!
image.png
就是顶部这个控制返回的胶囊按钮
image.png

内页返回探索

在设置了小程序的配置 navigationStyle: ‘default’ 通过NavgatorTo进入第二个小程序页面就有返回按钮的了,
那我们就可以打开小程序的时候是一个空白页,onLoad时候直接NavgatorTo跳转到我们的webview页面,这样webview就带返回按钮了。
这样做有一个尴尬的问题,
image.png
进入小程序就有一个切换效果,而且进入内页返回到最后一页就又到了这个空白页。

最终解决方案

要点

  • 两个webview,一个做首页,一个做内页;
  • 通过微信sdk window.wx.miniProgram.navigateTo api 在h5跳转到小程序的页面。

  • window.wx.miniProgram.navigateTo({url: ../h5/webview?weburl=http://192.168.31.98:10086/#/${path}}); url传递url path如果里包含?和&后边会被截断。

可以用这个encodeURIComponent加密传输

  1. wvNavTo = (path) => {
  2. function fixedEncodeURIComponent(str) {
  3. return encodeURIComponent(str).replace(/[!'()*]/g, function (c) {
  4. return '%' + c.charCodeAt(0).toString(16);
  5. });
  6. }
  7. // url传递url不能带?,否则Taro.getCurrentInstance().router.params获取不到?后边的参数
  8. let url = fixedEncodeURIComponent(`http://172.19.133.243:10086/#${path}`)
  9. window.wx.miniProgram.navigateTo({ url: `/pages/index/index?weburl=${url}` });
  10. }
  11. }
  • 微信sdk需要1.3.2以上的 可以用这个。
  • 在第二个webview里准备着接收url的方法,当通过sdk跳转过来后直接跳转到相应的url里,第一个webview同理
    1. componentDidMount() {
    2. try {
    3. // @ts-ignore
    4. let { weburl } = Taro.getCurrentInstance().router.params as string;
    5. weburl = decodeURIComponent(weburl);
    6. this.setState({
    7. weburl, //获取H5页面传递过来的weburl
    8. });
    9. } catch (error) {
    10. console.log("weburl set error",error);
    11. }
    12. }

原理解释

1、在小程序里的H5可以通过sdk跳转到小程序页面window.wx.miniProgram.navigateTo。
2、小程序设置navigationStyle: ‘default’ 通过NavgatorTo进入第二个小程序页面就有返回按钮的了,而且这个按钮对于webview容器里嵌套的网页返回也起作用,会执行webview goback操作,如果webview容器的页面栈为0层则退回上一个小程序页面,这些完全不需要自己判断。
3、我们把四个首页放到第一个webview里,所有的内页跳转都通过window.wx.miniProgram.navigateTo跳转到第二个webview里并进入对应的页面,这样webview内页就有返回按钮了,所有的内页进入首页都跳转到第一个webview里,这样首页也都没有返回按钮了。

小程序分享问题

  • 分享后使用postmessage发送一个请求,值为webview要分享的url;
  • webview容器接收这个值,放在state里做响应处理;
  • 注意postmessage并不是实时触发的,而是在特定的时机才会触发,小程序销毁,后退等,所以onMessage会收到多条数据,每次取最后一条就好了。
    // h5分享时调用这个方法,把要分享的url传递进去,包括拼接的特定的参数等。
    /**
    * 传递分享的url
    */
    setMinShareInfo: function (url) {
    var obj = {
      webshareurl: url,
    };
    window.wx.miniProgram.postMessage({ data: obj });
    },
    
// pages/index/index.tsx
import React, { Component } from 'react';
import { WebView, View } from '@tarojs/components';
import Taro from '@tarojs/taro';

import './index.scss';

/**
 * 作为webview内页
 */
export default class Index extends Component {
  state = {
    pathroute:'',
    weburl: '',
    shareUrl: `/pages/index/index?weburl=https://****.***.com/app/index.html#/pages/index/index?flagMini=InMini`,
  };

  componentDidMount() {
    try {
      // @ts-ignore
      let { weburl } = Taro.getCurrentInstance().router.params as string;
      weburl = decodeURIComponent(weburl);
      this.setState({
        weburl, //获取H5页面传递过来的weburl
      });
    } catch (error) {
      console.log('weburl set error', error);
    }
  }

  /**
   * 小程序右上角分享触发的事件
   *
   * @param res
   * @returns
   */
  onShareAppMessage(res) {
    return {
      title: '分享标题',
      path: this.state.shareUrl,
    };
  }

  handleMessage = val => {
    function fixedEncodeURIComponent(str) {
      return encodeURIComponent(str).replace(/[!'()*]/g, function(c) {
        return '%' + c.charCodeAt(0).toString(16);
      });
    }
    try {
      const data = val?.detail?.data
      // 取postmessage的最后一个
      this.setState({
        shareUrl: `/pages/index/index?weburl=${fixedEncodeURIComponent(
          data?.[data.length - 1]?.webshareurl,
        )}`,
        pathroute: data?.[data.length - 1]?.webshareurl.slice(40),
      });
    } catch (error) {
      console.log('handleMessage error: ', error);
    }
  };

  render() {
    return <WebView onMessage={this.handleMessage} src={this.state.weburl} />;
  }
}

参考

https://blog.csdn.net/t6546545/article/details/82809302