需求背景
webview中的h5页面做某些操作后需要修改当前的分享页面,但是,此时H5页面的链接没有改变。需要有一个机制通知小程序页面要修改分享参数了。
技术解决
根据小程序文档,支持webview中页面以postMessage的形式向小程序通信(仅在分享,返回等特殊操作时,才触发)
要点:
- 使用wx.miniProgram.postMessage向小程序通信,当用户转发时页面可以监听到消息
- 页面监听到的消息为历史列表,且不清除,需要自行处理
- 小程序页面设置了onShareAppMessage可以支持分享,默认分享为当前页面
- 用户点击转发后,会先触发webview的onMessage,再调用onShareAppMessage设置分享配置
h5和小程序约定的postMessage消息格式:
{
type:'消息类型',//setShareOption
...restData // 其他参数
}
复制代码
h5页面设置分享参数:
/**
* 设置weapp分享链接
* @param option
* @example setWeappShareOption({title:"分享标题",path:"分享链接",imageUrl:'分享图片可为空'})
*/
export function setWeappShareOption(option:{title:string,path:string,imageUrl:string}){
if(window['wx'] && window['wx'].miniProgram && window['wx'].miniProgram.postMessage){
window['wx'].miniProgram.postMessage({
type:'setShareOption',
title:option.title,
path:option.path,
imageUrl:option.imageUrl
});
}
}
复制代码
小程序页面支持分享:
import Taro from '@tarojs/taro'
import { WebView } from '@tarojs/components'
import {
getCurrentChannelEventHash,
resetChannelHash,
} from './WebViewHashChannelData'
import { unpackUrl,packUrl } from '../../src/core/UrlHelper'
const DEFAULT_WEB_PATH = 'https://xxx.x.com/xxx' // to 参数默认的域名前缀
/**
* 小程序Page页面
* @param to 要跳转的h5页面地址 e.g:/xxx/xxx or 完整地址 https://xxx....
* @param title 要跳转的h5页面标题
*/
export default class H5WebView extends Taro.Component {
config = {
navigationBarTitleText: ' ',
}
state = {
url: '', // url不支持Hash
hash: '',
}
isBack = false
shareOption = {
title: null,
path: null,
}
componentWillMount() {
this.isBack = false
const to = decodeURIComponent(this.$router.params.to || '')
const urlObj = unpackUrl(to)
let url = urlObj.pathWithSearch
let hash = urlObj.hash
let title = this.$router.params.title
if (title) {
title = decodeURIComponent(title)
Taro.setNavigationBarTitle({ title: title })
}
this.setState({
url,
hash,
})
console.log('[H5WebView] mount', url, hash)
}
componentDidShow() {
// onShow
if (this.isBack) {
// 小程序页面回退,通知h5
let channelEventHash = getCurrentChannelEventHash()
// if (!channelEventHash) {// TODO 仅白名单的页面增加backPush 通过postMessage??
// channelEventHash = getBackEventHash();// 通知游戏,上层的Page 移走了
// }
this.setState({
hash: channelEventHash,
})
// 通知完毕 重置
resetChannelHash()
console.log('[H5WebView] show set hash', channelEventHash, this.state.url)
} else {
console.log('[H5WebView] show first')
}
this.isBack = true
}
_getLastData(itemList, type) {
if (!itemList || itemList.length === 0) {
return null
}
let lastIdx = itemList.length - 1
while (lastIdx >= 0) {
let item = itemList[lastIdx]
if (item && item.type === type) {
return item
}
}
}
handleMessage(e) {
let { data } = e.detail
// 设置最后一条share
let shareOption = this._getLastData(data, 'setShareOption')
if (shareOption) {
this.shareOption = {
title: shareOption.title,
imageUrl: shareOption.imageUrl,
path: shareOption.path,
}
}
console.log('withWV handleMessage', data)
}
/**
* 支持分享消息
*/
onShareAppMessage() {
if (this.shareOption) {
let path = null
if (this.shareOption.path) {
path = packUrl(this.$router.path, {
to: this.shareOption.path,
title: this.shareOption.title,
})
}
return {
title: this.shareOption.title,
path,
imageUrl: this.shareOption.imageUrl,
}
}
}
onWebViewLoad(e) {
console.log('withWV onWebViewLoad', e.detail.src, this.state.hash)
this.shareOption.path = e.detail.src //默认分享页面为当前页面
}
_normalizeTo() {
let to = this.state.url
if (!to) {
return null
}
if (to.substr(0, 4) !== 'http') {
to = DEFAULT_WEB_PATH + to
}
if (this.state.hash) {
to += '#' + this.state.hash
}
return to
}
render() {
let to = this._normalizeTo()
if (!to) {
return null
}
return (
<WebView
src={to}
onMessage={this.handleMessage}
onLoad={this.onWebViewLoad}
/>
)
}
}
复制代码
TODO
可以在postMessage中加一个字段id,每次处理完后,记录上次处理的最后一条消息的id,下次处理从那条消息之后处理