微信小程序+webview+vue混合开发 传值踩坑

1  在vue h5页面中调用小程序原生页面 再从原生小程序页面带参数给 h5页面 

 问题 1:原生小程序如果要传递参数给 h5  只能通过webveiw 的url 传参,然而 url 改变 会生成一条新的访问记录(这里把webveiew 当做一个浏览器)

 问题 2:问题1 产生的这条新的访问记录 只能监听到 url 的改变   vue 的   生命周期方法 都不会执行 (这里只能在vue守卫中 将url上的参数放到localStorage中,再在对应的h5页面上 监听  根据约定好的key 监听value的变化,注意  这里需要监听 自定义事件 ,自定义事件可以监听的同一个页面的localStorage 的value变化  监听 h5原生得到 storage事件 只能监听不同页面的 localStorage 变化  这里 我已经将这两个监听进行了封装 可以兼容这两种情况)

 问题 3: 因为多产生了一条h5路由记录 所以这个要回退多次(次数由你在h5页面与原生页面来回改变多少次url 决定)

解决方案: 注意: vue h5页面 除了第一个外  必须使用  this.$router.push()   跳转页面 (否则,页面有可能跳转会混乱)

1  原生webview中 onShow方法中监听参数变化拼接到URL上 




 methods = {
    // webview 指向网页的链接。可打开关联的公众号的文章,其它网页需登录小程序管理后台配置业务域名。
    /*   网页向小程序 postMessage 时,会在特定时机(小程序后退、组件销毁、分享)触发并收到消息。
     e.detail = { data } */
    receivePostMessage: (res) => {
      console.log('-receivePostMessage-', res)
      let tokenInfo = wepy.globalData.getPostMessageByKey(res.detail.data, 'TOKEN_INFO')
      if (tokenInfo) { // h5中 postMessage token信息不为空 则覆盖小程序myUserInfo信息
        wepy.globalData.myUserInfo = tokenInfo
      }
    },
    // 网页加载成功时候触发此事件。e.detail = { src }
    monitorLoad: e => {
      console.log('-monitorLoad-', e)
      wx.hideLoading()
    },
    // 网页加载失败的时候触发此事件。e.detail = { src }
    monitorError: e => {
      console.log('-monitorError-', e)
      wx.hideLoading()
    }
  };

  onLoad(option) {
    wx.showLoading()  //开启加载webview loading 
    console.log('--option--' + JSON.stringify(option))
    this.option = option
    console.log('onLoad')
    // 超过5秒 没有加载完成h5 自动去除加载动画loading
    setTimeout(function() {
      console.log('setTimeout h5  hideLoading')
      wx.hideLoading()
    }, 5 * 1000)
  }

  onShow() {
    if (this.option) {
      this.resloveH5Url() // 监听url  及参数变化
    }
  }


// 解析 组装h5url
  resloveH5Url() {
    console.info('resloveH5Url token', wepy.globalData.myUserInfo)
    let option = this.option
    let url = decodeURIComponent(option.url)
    console.log('----url---' + url)
    if (url.indexOf(wepy.globalData.serviceConfig.webUrl) >= 0) {
      // 自家游自家小程序H5跳转 加token openId
      if (url.indexOf('?') > 0) {
        if (url.indexOf('token=') < 0) {
          url += `&token=0&openId=${wepy.globalData.wxUserInfo.openId}&realName=${wepy.globalData.myUserInfo.realName}&mobile=${wepy.globalData.myUserInfo.mobile}`
        }
      } else {
        if (url.indexOf('token=') < 0) {
          url += `?token=0&openId=${wepy.globalData.wxUserInfo.openId}&realName=${wepy.globalData.myUserInfo.realName}&mobile=${wepy.globalData.myUserInfo.mobile}`
        }
      }
      // 身份证信息
      if (url.indexOf('idCardInfo') < 0) {
        url += `&idCardInfo='{}'`
      }
      if (url.indexOf('driveCardInfo') < 0) {
        url += `&driveCardInfo='{}'`
      }
      if (url.indexOf('carCity') < 0) {
        url += `&carCity='{}'`
      }
      url += `&from=xcx`
      // 处理 扫描证件数据
      let scanType = wepy.globalData.scanType
      if (scanType) {
        if (scanType == 1) {
           // url += `&idCardInfo=${encodeURIComponent(JSON.stringify(wepy.globalData.idCardInfo))}`
          url = url.replace(
        /(\\?|&)idCardInfo=([^&|^ ]*)/,
        `$1idCardInfo= ${encodeURIComponent(JSON.stringify(wepy.globalData.idCardInfo))}`
      )
        } else if (scanType == 2) {
          // url += `&driveCardInfo=${encodeURIComponent(JSON.stringify(wepy.globalData.driveCardInfo))}`
          url = url.replace(
        /(\\?|&)driveCardInfo=([^&|^ ]*)/,
        `$1driveCardInfo= ${encodeURIComponent(JSON.stringify(wepy.globalData.driveCardInfo))}`
      )
        }
      }
      let carCity = wepy.globalData.carCity || this.carCity
      if (carCity && carCity.cityId) {
        url = url.replace(
        /(\\?|&)carCity=([^&|^ ]*)/,
        `$1carCity= ${encodeURIComponent(JSON.stringify(carCity))}`
      )
      }
      console.log('--token-', wepy.globalData.myUserInfo)
      url = url.replace(
        /(\\?|&)token=([^&|^ ]*)/,
        '$1token=' + wepy.globalData.myUserInfo.token
      )
      console.log('url', url)
    }

    this.h5Url = url
  }

2  vue h5页面   在router守卫中 将url上的参数  放到localStorage中  触发 localStorage 的值变化监听

router.beforeEach((to, from, next) => {
  store.commit('loadingEnd');
  console.warn(`【 ${from.meta.name} 】:来源路由地址${from.fullPath}`)
  console.warn(`【 ${to.meta.name} 】:当前路由地址${to.fullPath}`)
  console.warn(`【 router query 】: ${JSON.stringify(to.query)}
【router params 】:${JSON.stringify(to.params)}`)
  if(!from.fullPath||from.fullPath=='/'){ //第一次进入页面  清空先前token   
    console.log("清空token",from);
    localStorage.token="";
  }
  let  source = to.query.from ;
  if(source=='xcx'){
    if (!!to.query.token&&to.query.token!='undefined') {
      //localStorage.token = to.query.token||''
      localStorage.setItem('token',to.query.token||'');
    }else{
      //localStorage.token='';
      localStorage.setItem('token','');
    }
    if (!!to.query.openId&&to.query.openId!='undefined') {
      //localStorage.openId = to.query.openId||'';
      localStorage.setItem('openId',to.query.openId||'');
    }
    if (!!to.query.realName&&to.query.realName!='undefined') {
      //localStorage.realName = to.query.realName||'';
      localStorage.setItem('realName',to.query.realName||'');
    }
    if (!!to.query.mobile&&to.query.mobiel!='undefined') {
      //localStorage.mobile = to.query.mobile||'';
      localStorage.setItem('mobile',to.query.mobile||'');
    }
 
    if (!!to.query.idCardInfo&&to.query.idCardInfo!='undefined'&&to.query.idCardInfo!="'{}'") {//身份证
        localStorage.setItem('idCardInfo', decodeURIComponent(to.query.idCardInfo||'') )/* JSON.parse()  */  ;
    }
    
    if (!!to.query.driveCardInfo&&to.query.driveCardInfo!='undefined'&&to.query.driveCardInfo!="'{}'") {//驾驶证
      localStorage.setItem('driveCardInfo' , decodeURIComponent(to.query.driveCardInfo||'')) /* JSON.parse( )  */  ;
    }
     
    if (!!to.query.carCity&&to.query.carCity!='undefined'&&to.query.carCity!="'{}'") {//身份证
      localStorage.setItem('carCity', decodeURIComponent(to.query.carCity||'') )/* JSON.parse()  */  ;
    }
 
  }
  if(!!to.meta.needLogin){ //需要登录
    if(!!localStorage.token){ //小程序已登录 并且token 传过来了
      next()
    }else{ // 第一次进页面   App.$Login  无法调用
      App.$Login((res)=>{
        next()
      });
    }

  }else{ //不需要登录
    next()
  }

  
})

3 监听 localStorage 值变化


utils/index.js
export const dispatchEventStroage = (monitorKey) => { // 1 ** 放到main.js里执行
  const signSetItem = localStorage.setItem
  localStorage.setItem = function (key, val) {
    if (monitorKey && key == monitorKey) {
      let setEvent = new Event('setItemEvent')
      setEvent.key = key
      setEvent.newValue = val
      window.dispatchEvent(setEvent)
    } else {
      let setEvent = new Event('setItemEvent')
      setEvent.key = key
      setEvent.newValue = val
      window.dispatchEvent(setEvent)
    }
    signSetItem.apply(this, arguments)
  }
}

export const monitorDispatchEvent = (callback) => {    //2 **  放到  监听的页面 去执行 mounted
  // window全局监听localStorage的setItem事件以及时更新
  window.addEventListener('setItemEvent', function (e) { //1 监听自定义事件  不同页面监听不到 相同页面可以监听到 
    if (callback) {                                    //2 router 守护里触发 修改  需要监听 setItemEvent 自定义事件
      callback(e);   //  e.key   e.newValue
    }
  })
  window.addEventListener('storage', function (e) { // 1 原生事件    不同页面可以监听到  相同页面  监听不到  
    if (callback) {                                   // 2 a 页面修改  b页面监听  需要使用 storage原生事件 监听得到
      callback(e);   //  e.key   e.newValue
    }
  })
}
main.js  全局注册自定义   localStorage监听事件  用于监听 同个页面触发的 localStorage值变化( 上面的 vue router守卫中的localStorage变化 跟页面 算是同个页面。所以这里需要自定义事件 来解决监听同一个页面 触发localStorage值变化的需求)

dispatchEventStroage();




a.vue中 (及需要获取从小程序页面传值的页面)

  mounted() {

    monitorDispatchEvent((e)=>{
        if(e.key=="carCity"){
            let carCity = e.newValue ;
            if(carCity!="'{}'"){
               carCity = JSON.parse(carCity);
               this.zcInfo=this.zcInfo||{};
               this.zcInfo.cityCode = carCity.cityCode;
               this.zcInfo.cityName = carCity.cityName ;
            }
        }
    })

  }

4  App.vue 中监听onhashchange  onpopstate(此方法行不通,尤其是当页面上有多个跳转带点的时候,1:needNavigateBack 监听到的是上个页面的值,因为popstate事件监听执行  要早于 vue里面router的监听执行 ,跳转混乱

data(){

  return {

     needNavigateBack:false  //需要跳出webview
   }
},



created() {

    this.monitorRouterBack();
    this.monitorRefreshWebViewForXcx();

},


methods:{


 /**
     * hash路由回退监控 
     * 基于hash 的路由 第一次会存储两条路由 回退就会回退两次 这里进行监听处理
     */
    monitorRouterBack(){
    window.onhashchange = function(event){
      // console.log('onhashchange',event.oldURL, event.newURL);
      let newUrl = event.newURL;
      let target = "/#/";
/*     if(newUrl.lastIndexOf(target)==newUrl.length-target.length){
       //history.go(-2)
    } */
    let  newU = event.newURL.split('?')[0];
    let  oldU = event.oldURL.split('?')[0];
    console.log('newU',newU);
    console.log('oldU',oldU);
    if(newU==oldU||newUrl.lastIndexOf(target)==newUrl.length-target.length){ //需要跳转出webview
      //1 目标 跟  来源是一个页面
      //2 目标页面是 http://192.168.4.168:8080/app-xcx-h5/#/
      this.needNavigateBack=true ;
    }else{
      this.needNavigateBack=false;
    }
    }
    },
    monitorRefreshWebViewForXcx(){
        //每次小程序切换前后台,都会修改history栈,触发popstate事件,并且增加history栈的长度,
  //而点击返回按钮虽然会触发popstate事件,但是并不会修改堆栈长度,通过这个区别知道用户是想点击返回退出正文,调用小程序的返回接口
      let oldHistoryLen = history.length;
      window.onpopstate=()=>{
        console.log('onpopstate oldHistoryLen',oldHistoryLen);
        console.log('onpopstate history.length',history.length);
        alert('onpopstate'+JSON.stringify(history))
        if(history.length!=oldHistoryLen){
            alert('onpopstate 1')
          oldHistoryLen = history.length;
        }else{
            alert('onpopstate 2')
            alert('onpopstate oldHistoryLen'+oldHistoryLen)
            alert('onpopstate history.length'+history.length)
          if(oldHistoryLen>=2&&this.needNavigateBack){
             alert('onpopstate 3')
            //alert('onpopstate 触发')
            console.log('onpopstate 触发 oldHistoryLen',oldHistoryLen)
            wx.miniProgram.navigateBack();
          }
        }
      }
    },


}

 

方案4 的有效替换 (至少在我的方案中没问题)有效方案

场景描述

1 租车首页(h5页面 webview加载  goH5)  carIndex   

2 城市跳转 select-city (小程序原生页面)

 

goH5 页面




步骤
1 select-city 选择城市方法里面调用

      // 销毁先前的 webview
      let pages = getCurrentPages() // 当前页面
      let beforePage = pages[pages.length - 2] // 前一个页面
      let url = beforePage.data.h5Url // 前一个页面当前加载的url
      beforePage.setData({h5Url: ''})
      // goH5页面中会自动拼接 h5Url+最新参数
      wepy.globalData.carCity = item

这样  选择城市回退的时候 就会销毁掉先前的url  历史栈 , 只保留最新这个

如果页面中有多个 原生的跳转点需要更新数据会刷,那么需要自己在h5页面中保存 先前选中的数值

 

转载于:https://my.oschina.net/mirclewang/blog/3095124

你可能感兴趣的:(微信小程序+webview+vue混合开发 传值踩坑)