如何计算访问官网到下载app并注册的实际转化率?

前言

我们项目开发了一个安卓的app,目前没有上架打算,但会花钱进行推广,产品提了一个需求,需要我们去计算用户通过推广链接进入官网的用户量,到官网下载app安装后注册的转化率,用以计算在哪个渠道推广的效率和性价比更高。

这类需求有一个相对简单的方案,就是在下载前强制要求用户在官网注册,用户注册前记录一个访客,注册时将访客关联用户,下载后记录此用户已下载app,这种就算是个相对有效的转化率计算方案,也是个相对成熟的方案,同时我们产品的官网也支持注册,只需要加个强制注册的逻辑就行了。但产品提出,不希望在官网强制注册后下载,担心会降低下载量,在这个前提下,实现浏览器官网下载app到app内注册的转化

分析需求

  1. 用户在官网访问时,创建一个访客
  2. 下载app时调用接口,该访客已下载app
  3. 用户安装app并打开,读取到浏览器中的访客(?),注册接口将访客Id传上去做关联

技术方案

方案一:使用设备唯一标识

我们第一时间考虑的是,是否可以通过某种设备唯一标识来作为桥梁,如果浏览器和 App 都能读取同一个设备 ID,那么就可以准确地将两者关联。然而,受限于浏览器安全机制,前端 JavaScript 无法获取设备硬件标识符(如 IMEI、Android ID 等),这些信息只能在原生 App 环境中获取。即便通过某些指纹识别技术,也只能依赖浏览器行为生成的 fingerprint,而这种标识在浏览器更换、隐私保护等场景下不可靠。因此该方案不可行

方案二:利用剪切板传递访客 ID(最终采用)

最终采用了一种可操作性较高、实现成本较低的方案:

  1. 用户首次访问官网时生成访客 ID,保存在 localStorage 中;
  2. 当用户点击下载按钮时,将该访客 ID 复制到剪贴板;
  3. 用户安装 App 并首次打开时,App 读取剪贴板内容,从中获取访客 ID;
  4. 在用户注册时,携带该访客 ID 与注册信息一并提交,实现关联。

该方案的优势:

  • 不依赖浏览器或系统层的硬件信息
  • 实现简单,部署成本低
  • 不影响用户下载体验(无需登录/注册)

缺点:

  • 如果用户使用多个浏览器访问,可能生成多个访客 ID,导致统计偏差;
  • 如果用户在下载后复制了其他内容,可能导致剪贴板中的访客 ID 被覆盖;
  • 某些浏览器/系统可能限制剪贴板访问。

在与产品沟通后,确认允许一定程度的误差,因此我们采用了此方案。

代码实现

官网

// useVisitorRecord.js
import { clientPost, clientPut } from '~/api/request'
import isMobile from '~/utils/isMobile'

const userSource = useUserSource()
const visitorRecordKey = 'XX_Visitor_Id'
const visitorIdPrefix = 'XX_VISITOR_ID_'

const getLocalVisitorId = () => {
  const visitorId = localStorage.getItem(visitorRecordKey)
  if (visitorId) {
    return visitorId.replace(visitorIdPrefix, '')
  }
  return ''
}

const setLocalVisitorId = (visitorId) => {
  localStorage.setItem(visitorRecordKey, visitorIdPrefix + visitorId)
}


const createVisitor = async () => {
  const res = await clientPost('/visitor', {
    clientType: isMobile() ? 2 : 1
  })
  console.log('createVisitor', res)
  if (res && res.code === 200) {
    setLocalVisitorId(res.data)
  }
}

const updateVisitorInfo = async (params) => {
  const visitorId = getLocalVisitorId()
  if (visitorId) {
    await clientPut(`/visitor/${visitorId}/download-app`, {
      clientType: isMobile() ? 2 : 1,
      ...params
    })
  }
}

const copyVisitorId = () => {
  const visitorId = getLocalVisitorId()
  if (visitorId) {
    const copyText = visitorIdPrefix + visitorId
    if (navigator.clipboard) {
      navigator.clipboard.writeText(copyText)
    } else {
      const copyTextDom = document.createElement('input')
      copyTextDom.value = copyText
      document.body.appendChild(copyTextDom)
      copyTextDom.select() // 选取文本内容;
      document.execCommand('Copy')
      document.body.removeChild(copyTextDom)
    }
  }
}

export const useVisitorRecord = () => {
  return {
    getLocalVisitorId,
    copyVisitorId,
    createVisitor,
    updateVisitorInfo
  }
}

// 下载按钮点击事件
const handleBtnClick = () => {
  const visitorRecord = useVisitorRecord()
  visitorRecord.copyVisitorId()
  visitorRecord.updateVisitorInfo()
}

app中读取剪切板,这里使用的是uniapp实现

const visitorRecordKey = 'XX_Visitor_Id'
const visitorIdPrefix = 'XX_VISITOR_ID_'
export const useUserStore = defineStore('user', {
  state: () => ({
    // 访客id
    visitorId: ''
  }),
  actions: {
    // 初始化本地访客id
    async initLocalVisitorId() {
      const visitorId = uni.getStorageSync(visitorRecordKey)
      if (visitorId) {
        // 先取本地访客id
        this.visitorId = visitorId.replace(visitorIdPrefix, '')
      } else {
        // 如果本地没有访客id,则从剪贴板中获取
        await this.getVisitorIdFromClipboard()
      }
      if (this.visitorId) {
        this.updateVisitorInfo()
      }
    },

    // 从剪贴板中获取访客Id
    async getVisitorIdFromClipboard() {
      return new Promise(resolve => {
        uni.getClipboardData({
          success: res => {
            if (res.data.startsWith(visitorIdPrefix)) {
              this.visitorId = res.data.replace(visitorIdPrefix, '')
              uni.setStorageSync(visitorRecordKey, res.data)
            }
          },
          fail: err => {
          },
          complete: () => {
            resolve()
          }
        })
      })
    },

    // 创建访客
    createVisitor() {
      const appStore = useAppStore()
      userApi
        .createVisitor({ clientType: 3 })
        .then(res => {
          if (res) {
            this.visitorId = res
            uni.setStorageSync(visitorRecordKey, visitorIdPrefix + this.visitorId)
          }
        })
    },

    // 更新访客信息
    updateVisitorInfo() {
      const appStore = useAppStore()
      if (!this.visitorId) {
        return
      }
      userApi.updateVisitor({
        visitorId: this.visitorId,
        // 访客访问了app
        accessApp: true
      })
    }
  }
}

总结

这算是个比较简单的实现方案,而且免费,但限制也确实存在。但我目前没想到过其他比较好的方案,如果有更好的方案欢迎分享,我来学习学习