前言
如今的互联网环境下,营销活动中针对APP、小程序、H5等不同渠道的页面,希望提供一个统一的二维码链接,并支持动态参数透传。比如:
https://aaa.com/forwards?scene=activity&activityId=123
一、搭建配置平台
以cms为例,手动搭建一个cms配置平台,提供场景scene、渠道channel、渠道url、页面类型contentType、扩展参数extendParams。
二、渠道跳转支持
以小程序为例,微信扫码后,需要拉起小程序并打开映射的原生页面或H5页面,同时将透传参数拼接到指定链接上。
1、小程序管理后台配置映射
我们需要在小程序后台配置普通二维码域名映射,保证用户使用微信扫一扫后,能拉起微信小程序。
2、渠道参数处理
小程序创建中间页 /pages/common/middle-page/middle-page;
中间页处理固定参数,比如source=transferActivity;
onLoad(options){
// 获取普通二维码链接,当扫普通二维码进入时 options.q 才有值
const scanUrl = decodeURIComponent(options.q)
console.log('scanUrl========', scanUrl)
if (scanUrl) {
this.handleScanUrlFormQRCode(scanUrl)
}
}
async handleScanUrlFormQRCode(scanUrl){
// 通用规则二维码
const forwardReg = new RegExp('forwards')
// 判断是通用二维码链接 https://aaa.com/forwards?scene=activity
if (forwardReg.test(scanUrl)) {
// 获取二维码url场景参数
const scanUrlParams = getUrlParams(scanUrl)
// 根据scene查询cms配置平台的配置信息
const [forwardsInfo = {}] = await queryCmsForwardsInfo(scanUrlParams.scene)
const { url = '', contentType = '', extendParams = '' } = (forwardsInfo.routes && forwardsInfo.routes[0]) || {}
// cms配置的扩展参数
const extObj = JSON.parse(extendParams)
// 判断是否跳转webview容器页面
const isWebViewType = contentType === 'webview'
// 接口请求回来的url,拼接在二维码携带的参数透传给h5
const link = `${url}${url.indexOf('?') > -1 ? '&' : '?'}${scanUrl.split('?')[1] || ''}`
// cms配置的渠道页面url的参数
const {dest = '', source = '', type = ''} = getUrlParams(url)
// 去掉扫码链接上的scene,除scene外的参数都需透传给原生页
delete scanUrlParams.scene
// 整合cms配置的扩展参数和扫码链接上的参数
const destExtObj = Object.assign({}, extObj, scanUrlParams)
// cms配置路径:/pages/common/middle-page/middle-page?source=transferActivity
if (source === 'transferActivity') {
// 这里的href代表的是cms配置的目标h5页面的链接
const { href: extHref, ...other } = destExtObj || {}
// 将所有参数拼接到目标链接上
const nextHref = genUrl(extHref, destExtObj)
// 跳转webview容器页,打开目标h5
const destUrl = `/pages/common/webview/webview?href=${nextHref}`
wx.navigateTo({
url: destUrl
})
}
}
}
/**
* 对 url 查询参数进行增/删/改,生成新的 url
* @param { string } url 需要处理的 url
* @param { Query } [params] 需要处理的参数对象
* @param { <T extends Query, K extends keyof T>(value: T[K]; key: K; params: T) => boolean } [filter] 参数筛选器;返回 true 保留该参数; 返回 false 删除该参数
* @returns { string } 新的 url
*/
export const genUrl = (url, params, filter) => {
const genPairs = (key, val) => {
const name = encodeURIComponent(String(key))
const value = encodeURIComponent(String(val))
return `${name}=${value}`
}
/** @type {typeof filter} */
const defaultFilter = val => val !== null && val !== undefined
const includes = typeof filter === 'function' ? filter : defaultFilter
const href = String(url).trim()
const prevParams = getUrlParams(href)
const nextParams = Object.assign({}, prevParams, params || {})
const keys = Object.keys(nextParams).filter(key => includes(nextParams[key], key, nextParams))
const qs = keys.map(key => genPairs(key, nextParams[key])).join('&')
const index = href.indexOf('?')
const search = index < 0 ? '' : href.slice(index + 1)
let base = ''
let hash = ''
if (search) {
const i = search.indexOf('#')
hash = i < 0 ? '' : search.slice(i)
base = href.slice(0, index)
} else {
const i = href.indexOf('#')
hash = i < 0 ? '' : href.slice(i)
base = i < 0 ? href : href.slice(0, i)
}
return [base, qs ? `?${qs}` : '', hash].join('')
}
/**
* 获取URL中的所有查询参数
* @param {string} url
* @param {boolean} [autoDecode=true]
* - 解析参数项时,是否进行一次 decodeURIComponent,默认为 true
* - 只有你明确知道传 false 的场景时才需要传 false,否则请保留默认行为
* @returns {Record<string,string>}
*/
export const getUrlParams = (url, autoDecode = true) => {
const params = Object.create(null)
if (typeof url !== 'string') {
return params
}
const href = url.trim()
const index = href.indexOf('?')
if (index < 0) {
return params
}
const search = href.slice(index + 1)
const qs = search.split('#')[0] || ''
const decode = value => {
if (!autoDecode) {
return value
}
return decodeURIComponent(String(value).replace(/\+/g, '%20'))
}
const reducer = (acc, item) => {
if (!item.length) {
return acc
}
const i = item.indexOf('=')
const name = decode(i === -1 ? item : item.slice(0, i))
const value = decode(i === -1 ? '' : item.slice(i + 1))
return Object.assign(acc, { [name]: value })
}
// eslint-disable-next-line prettier/prettier
return qs.split('&').filter(Boolean).reduce(reducer, params)
}
三、配置示例
扫码后打开如下地址:
https://bbb.com?scene=wxappActivity¶mString=channel%3Dwxapp%26id=777
这个扫码后跳转的目标地址带了两个参数,场景scene和复合参数paramString。
注意:这里paramString是跳转链接希望从渠道端(如微信小程序)带过去的参数,也可以是任意其他参数,比如
https://aaa.com/forwards?scene=activity&activityId=123
cms配置平台配置:
scene: activity
channel: wxapp
url: /pages/common/middle-page/middle-page?source=transferActivity
contentType: native
extendParams: {"href":"https://bbb.com?scene=wxappActivity"}
对外提供扫码链接
https://aaa.com/forwards?scene=activity¶mString=channel%3Dwxapp%26id=777
需要注意的是,流程中涉及3个链接:扫码链接、渠道页面链接、跳转的目标链接。