用笨办法应对充满不确定性的未来

如何使 H5 页面跳出微信客户端自动在默认浏览器中打开

2023.08.24

TL:DR;

我们的一个业务流程需要通过微信来传播,用户收到分享卡片后点击后需要在系统浏览器中打开。这个过程会比较长。因此我们用一个 hack 技巧让 H5 页面自动跳出 Android 微信客户端。

问题

试想你作为一名用户,在微信上收到一个链接后需要点击 4 次以上才能真正打开页面,会不会觉得非常烦躁。我们可以说这个操作步骤的成本比较高,对产品体验影响比较大。因此前端对于复杂操作是需要尽可能避免的。

光看文字可能还不够直接,下面我们以一个微信 H5 支付场景举例,整个操作流程如下:

用户在 Android 微信上的操作流程非常繁琐,需要简化

用户在 Android 微信上的操作流程非常繁琐,需要简化

上图中可以看到,用户在 Android 微信上,想要跳转到默认浏览器中执行业务流程,至少要 4 步骤:

  1. 通过点击分享消息卡片等途径打开网页
  2. 点击微信界面右上角“…”图标打开分享菜单
  3. 在分享菜单中滑动,寻找到在默认浏览器中打开的选项
  4. 选择合适的浏览器图标并点击
  5. 最后一步才是在默认浏览器中执行流程

这样长的操作流程无疑是很难受的。

是真正的问题吗?

用户操作流程过长会严重影响用户的打开率,需要想办法解决这个问题。

在上一节的步骤分析中,我们发现第一步和第五步是不可或缺的,如果能优化掉第 2~4 步,就能够大幅度减少用户操作流程,提升用户体验。最大的价值是很多在产品层面上由于交互成本过高而不能投入的事情,就变得可以考虑了。

探索解决办法

方法一:弹出浮层提示用户手动操作

步骤:

在页面上增加一个引导浮层,例如一个指向屏幕右上角的箭头,提示“请点击右上角在浏览器中打开”

结果:

提示是比较直接,用户最终还是需要手动去操作,并不能减少用户操作次数,仅仅是给了用户一个操作预期。

我们期望的是能够减少用户点击次数。

方法二:模拟文件下载让微信自动弹出在浏览器中打开的窗口

步骤:

核心思路是在服务端处理,构造一个路由 */pages/a*, 当用户访问时先检测是否在微信中打开,如果是则返回以下 HTTP header,如果不是则不添加任何字段,返回标准的 HTML 页面。

self.send_header('Content-type', 'text/plain; charset=utf-8')
self.send_header('Content-Disposition', 'attachment; filename="downloaded.txt"')

结果:

以下是依据该思路编写的 Python 示例代码

from http.server import BaseHTTPRequestHandler, HTTPServer


class MyRequestHandler(BaseHTTPRequestHandler):

  def do_GET(self):
    user_agent = self.headers.get('User-Agent', '').lower()
    if 'micromessenger' in user_agent:
      if 'android' in user_agent:
        self.send_response(200)
        self.send_header('Content-type', 'text/plain; charset=utf-8')
        self.send_header('Content-Disposition',
                         'attachment; filename="downloaded.txt"')
        self.end_headers()
        self.wfile.write('这是在 Android 微信中的文本文件内容。'.encode('utf-8'))
      elif 'iphone' in user_agent or 'ipad' in user_agent:
        self.send_response(200)
        self.send_header('Content-type', 'text/plain; charset=utf-8')
        self.end_headers()
        self.wfile.write('这是在 iOS 微信中的文本文件内容。'.encode('utf-8'))
      else:
        self.send_response(200)
        self.send_header('Content-type', 'text/plain; charset=utf-8')
        self.send_header('Content-Disposition',
                         'attachment; filename="downloaded.txt"')
        self.end_headers()
        self.wfile.write('这是在微信中的文本文件内容。'.encode('utf-8'))
    else:
      self.send_response(200)
      self.send_header('Content-type', 'text/plain; charset=utf-8')
      self.end_headers()
      self.wfile.write('不是在微信中访问'.encode('utf-8'))

  def do_HEAD(self):
    self.send_response(200)
    self.send_header('Content-type', 'text/html')
    self.end_headers()


def run(server_class=HTTPServer, handler_class=MyRequestHandler, port=8000):
  server_address = ('', port)
  httpd = server_class(server_address, handler_class)
  print(f'Starting server on port {port}...')
  httpd.serve_forever()


if __name__ == '__main__':
  run()

示例链接: 部署在Replit 上的DEMO

上面代码中做了一些优化:

  • 返回正确的文件编码为 utf-8,避免浏览器显示中文网页时乱码的问题
  • 区分设备类型,只在 Android 微信中返回 HTTP 文件下载头,因为 iOS 微信无法弹出默认浏览器,还是需要用户手动操作。
  • 将返回的文件类型后缀改成.txt 纯文本,避免下载弹窗上展示出 PDF 的图标纯文本,避免下载弹窗上展示出 PDF 的纯文本,避免下载弹窗上展示出 PDF 的图标图标纯文本,避免下载弹窗上展示出 PDF 的图标

在微信中打开该链接,可以观察到在安卓微信上会自动弹出浏览器窗口,而在 iOS 上则展示正常的页面。

收益:

能够节省用户的两次点击,在我们的业务场景中,原本需要点击 4 次才能完成业务流程,现在直接缩短了一半。这是比较大的提升。

总结

最终我们选择了模拟文件下载的方式,并且提供了 DMEO 链接供直观体验。但是这种方式是依赖与微信对文件下载的实现,截止至 2023 年 8 月 24 日,这个方法还是有效的,但是不排除以后微信更新相关逻辑的情况。使用时请注意微信版本号的问题,以及可能存在的风险。