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();
}
}
}
},
}
5 方案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页面中保存 先前选中的数值