Intro
既然讲的是探索之路,重在一个过程,就以时间线的顺序来讲述,大概有这么些时间节点
- 丁香医生 H5 需求迭代中曾遇到过唤起 App 这个场景
- 丁香直播 H5 唤起 App 需求
- F2E 松鼠会 Review 活动
- 「统一的 H5 唤起 App 方案」项目
历程
丁香医生 H5 需求迭代
丁香医生 H5 已经上线挺久时间的了,对于有一些历史的项目,接到需求后我往往是去项目现有代码中找类似实现,看看能不能“代码复用”(通俗来说就是抄过来)。
某一次需求迭代中恰好遇到了唤起 App 这个场景,先是在项目中找到一处差不多的跳转实现,再使出粘贴复制的常规操作给“代码复用”了一下,为了保证高质量的交付,即使最简单的功能也自测一下,跳转 OK,非常成功,顺利提测然后就可以接着做下一个需求了。
丁香直播 H5 唤起 App 需求
后来参与了一段时间丁香直播 H5 项目,刚起步不久,“代码复用”这个操作不太好使,没有东西抄那怎么办呢,我们还可以采用“跨项目级别的代码复用”(给其他项目代码抄过来),顺利交付了不少产品需求。
某天丁香直播也出了唤起 App 的需求,作为一款平台产品,需要支持「丁香医生」、「丁香园」两款 App 的跳转,并且「丁香园」App 还需要支持「微信开放标签」跳转方式。
根据经验来说这个场景处理起来问题不大,不过是多参考几个项目罢了。
接入「微信开放标签」
什么是「微信开放标签」?
微信开放标签是微信公众平台面向网页开发者提供的扩展标签集合。通过使用微信开放标签,网页开发者可安全便捷地使用微信或系统的能力,为微信用户提供更优质的网页体验。—-《微信官方文档-公众号-开放标签说明文档》
这段定义可以说是比较糟糕的,除了让网页开发者感知到 666 以外根本没说明白是个什么东西,我基于这个简介做一些扩充。
「扩展标签集合」其实就是一些 HTML 标签,只能在微信的 webview 中使用,目前有:
<wx-open-launch-app>
用于页面中提供一个可跳转指定App的按钮<wx-open-launch-weapp>
用于页面中提供一个可跳转指定小程序的按钮
通过两个真实的故事我们来看一下「微信开放标签」到底体验有多么“优质”。
第一个故事:一位手持安卓手机的朋友一直想拥有一台 iPad Air,某天在微信薅羊毛群点开群友分享的「什么值得买」H5 页面
- 微信 H5 点击「下载 App 领取」—> 页面跳转提示右上角浏览器打开
- 根据提示点击到浏览器打开
- 点击浏览器的 confirm 框「确定」 —> 成功唤起应用,看了三秒开屏广告之后,停留在首页
- 当发现停在首页不动之后,点击搜索框搜索关键词 ipad
- 滑动长列表筛选是哪一个活动
看到这里我们先思考几个问题
- 是什么支撑这这位用户走完整个流程?
- 作为用户的我们在遇到这类唤起不顺畅场景的时候会怎么做?
- 我们公司的 App 存在这样的场景吗?
第二个故事:一位爱学习的朋友在丁香直播平台学习 SEO 相关知识
点击顶部广告位,弹出了微信原生的提示框,引导我们去「丁香园客户端」,点击确认后直接跳转到丁香园 App 的直播页面,可谓“One Step”,一步触达,非常顺畅。
这就是「微信开放标签」的唤醒体验。
虽然好像有一些不对劲,跳转到了「近视不想戴眼镜xxxx」,这里只是随便截了两张图作为示例。
对比之后,不禁感叹,wow awesome,「微信开放标签」的体验是真的优质!
然而用户优质体验的背后却是开发者坎坷的接入,标签接入过程历时三四天,问题重重(当然一部分原因也是鄙人水平有限),详细过程此次不展开,主要碰到了以下这些问题:
- 公司内部还没有「微信开放标签」接入文档可以参考,具体如何接入需要找有经验的同学单独请教。
- 通过调试
wechat-sdk
这个包发现,目标公众号没有接入wechat
系统,根据wiki
文档求助平台组的后端同事后才接入成功。 - 没有了解公司内各个系统的渠道,比如
SIM
是什么,wechat
系统又是什么,wechat-sdk
调用这个接口有什么作用,这些疑问没有在文档中没有找到答案。理想情况是不需要深入到代码也能清楚各个系统的职能、调用链路。举个例子,Jarvis
大家都知道,介绍Jarvis
的时候会提到公司各个服务间的职能和调用关系,得先对各个系统有个概念,后续才是怎么用。
事成之后,将经验总结成了一篇文档放在 wiki 上 简单聊一聊「微信开放标签」,借此机会我特别写了一段代码来感谢 tod、丁妈团队部分同学的协助:
if (response === '扔过来 gitlab 地址叫我自己看') {
return '一般感谢'
} else if (response === '一步步耐心解答') {
return '非常感谢'
}
隐约觉得有优化空间,根据「单一出口」原则,再简单重构了一下
const thanks = '感谢'
let howThanks = ''
if (response === '扔过来 gitlab 地址叫我自己看') {
howThanks = '一般'
} else if (response === '一步步耐心解答') {
howThanks = '非常'
}
return `${howThanks}${thanks}`
言归正传,简单总结一下「微信开放标签」的特点:只可以在微信 webview 内使用,IOS 和安卓下的 App 唤起体验一致,可以直接触达应用深层页面,前提是 App 同学也需要支持一下。
然而「微信开放标签」有一个限制:同一域名只能同时绑定一个移动应用。
这也就意味着只能对「丁香园」App 起作用,唤起「丁香医生」App 还是得走传统的方式,且「微信开放标签」只能在微信浏览器环境内用,其他浏览器环境的唤起操作也得走传统方式,便开启了传统 H5 唤起 App 方案的调研。
传统方案调研
思路依旧是“跨项目级别的复用”,打开「丁香园」、「丁香医生」的项目,看看项目代码是如何实现的,代码复制粘贴过来,并且在真机上打开页面跑一跑。
过程中发现几年前就有一名同学将唤起 App 场景封装成了 dxy-get-install
这个包,并且还配有 wiki-H5 唤起 App 文档,得知这个消息我非常的开心,因为最喜欢给现成模块导进来直接用的感觉。
试用了一下发现经常给跳转到「丁香无线」的页面,正好介绍一下,「丁香无线」可以理解为应用的主页,提供了下载应用的功能,下图是「丁香园」的「丁香无线」页面:
这次的调研总的来说就是各种方案在真机上反复尝试,并没有深入到调用的原理(毕竟一杯茶、一包烟、一个原理学一天)。在用户体验上力求跳转的方案能做到「操作路径最短」、「进入 App 内能够打开指定页面」,最终产出了一张“最适合”(以当时的视角看)「丁香直播」项目的流程图:
总的来说,本次需求开发的比较痛苦,原理没空搞明白,大多时间花在了真机测试效果上。其实大多产品其实也不那么关心是怎么跳过去的,毕竟需求就是一句话「跳转到 App」,用什么技术方案更多是开发在纠结。
这次需求过后也是产生了一些将「get-install」这个包改造或者对「微信开放标签」加以封装的想法,但仅仅是一些没有深入思考的想法,并没有付出更多精力研究,再后来,时间流逝,想法一直搁置…
F2E 松鼠会 Review 活动
松鼠会第三期活动是 CodeReview,有 review dxy-get-install
这个包的提议,勾起了我的回忆,决定打造生产力,优雅唤起 APP。
开始构思「统一的 H5 唤起 App 方案」。
首先是探究 dxy-get-install
这个包的在实际使用情况中都有哪些问题。
Jarvis 提供了「依赖模块统计」的功能,可以查询到依赖 @dxy/dxy-get-install
这个模块的项目。
丁香医生 H5 项目最新的一处使用,特地避开了 get-install,并且标注了不使用的两点原因。
再看一处调用,仅仅一个小分支里用到了 get-install
。
tod 则是封装了一个 tod-utils
npm 包,类似 dxy-get-install
实现了一个唤起 App 的一整套方法,最终暴露工具函数 openScheme
用于 tod 业务线 App 的唤起。
在调研完五六个项目,十来处调用后总结了一些问题:
- 不使用甚至故意避开 get-install 这个包,手动实现链接拼接、环境判断、App 唤起的逻辑
- 仅在部分逻辑分支(浏览器环境)下使用 get-install 来实现 App 唤起
- get-install 内部存在一些逻辑小问题,如:使用 http 链接,导致了一些跳转流程/体验上的不顺畅
- 第三方的模块使用没有再统一封装一层,项目内部跳转方法使用混乱
更为详细的调研报告请看 -> 现有 H5 跳转 App 方案调研。
在松鼠会的第一次 Review 活动上,同学们指出了代码层面的问题,详见 第三期 Code Review 活动-第一次会议纪要。
会后跟与组织者聊了聊「统一的 H5 唤起 App 方案」的构想,对想法大力支持,并拉了两个帮凶,一行四人开始了这个项目。
项目启动
项目开始的第一件事便是开会,明确项目成果的形态,以及成果如何服务于丁香园前端团队
- 一套最适合丁香园系应用的跳转流程方案
- 可以做到各个环境下的「最短流程」访问
- 一个 npm 包,通过「最简单的调用方式」以「最短流程」访问到「应用内具体页面」
- 解决现有问题
- 优化现状,「最短流程」和「应用内具体页面」方便了用户,理论上讲是可以提高业务的转化率。「最简单的调用方式」则是方便了开发者
- 一篇包括所有丁香园系应用跳转链接汇总的 wiki 文档
- 方便同学们查找
- 技术原理解析
- 帮助所有前端同学更好的理解这件事情的本质,搞明白原理
- 省去大家自己调研技术方案的时间,提高相同场景开发效率
原理解析
探究过程中阅读的文章非常多,其中有两篇文章非常推荐给大家,这两篇文章写得实在是太好了
在这个环节需要弄懂以下这么多概念
- Deep Linking
- URI Scheme
- Universal Link
- Android App Link
- Chrome Intent
- 丁香无线
- 应用宝
Deep Linking 只是一个概念, 是指通过一个链接进入另一个网站/App,并直接浏览其内部的某个页面。URI Scheme, Universal Links, Android App Links, 以及 Chrome Intent 都是为了解决从 Web 页面 Deep Linking 到 App 而做的尝试。 -《Deep Linking:从浏览器调起 APP》
以「丁香医生」App 的问医生页面为例,看几种链接形式
链接形式 | 链接 |
---|---|
URI Scheme | dxyaspirin://app.dxy.cn/aspirin/nativejump?type=finddoctor |
Universal Link | https://app.dxy.cn/aspirin/nativejump?type=finddoctor |
Android App Link | https://app.dxy.cn/aspirin/nativejump?type=finddoctor |
- URI Scheme
- 协议名是开发者自己命名,路径可以表示具体要打开的页面
- Universal Link
- 是一个普通的,可以用任何浏览器打开的 Web URL,在 iOS >= 9 的系统中这些 URL 可以绑定到对应 App 中,如果安装有对应 App 就会调起,否则会直接打开该 URL
- 没有安装应用也会打开 URL,那么 URL 是什么页面呢,以丁香医生为例,打开的就是「丁香无线」页面
Android App Link
- 与 Universal Link 相似,也是一个普通的 Web URL
- 在某些场景链接到这个 URL 时,会提示使用应用直接打开,类似电脑上装了很多播放器,点开一段视频,提示我们使用哪个来播放一样
- 下图小米手机在短信打开 App Link 的效果
对于国内大多的 Android 用户来说,很多时候能否起作用取决于所使用的系统是否支持(如小米的 MIUI、华为 EMUI) —- 丁香医生 App 同学
Chrome Intent 丁香园系应用没有支持,这里不展开
- 应用宝全称「腾讯应用宝」,腾讯家的应用市场,开应用市场的给我感觉就是收租金,挺来钱的,腾讯为了给自家应用市场导流(原因之一),提供了以应用宝作为中间页唤起 App 的方式
- 应用宝唤起的示例
- 应用宝唤起的示例
《Deep Linking:从浏览器调起 APP》总结了一些唤端技术的使用限制
平台和版本要求已经让唤起 App 这事够复杂了,以腾讯为代表的流量巨头们,还要来插一脚,在应用内对唤端技术加以更严格的限制,这就让事情变得更加复杂。
即使困难再多,方法总是有的:
引用一下社区上反响还不错的 H5 唤起 App 工具包 callapp-lib 的作者文章 H5唤起APP指南(附开源唤端库) 中的片段:
开工前的准备
了解到社区上有一些现成方案,于是对这些项目展开一些调研。
callapp-lib
是一个 Github 上 1500 star 的项目,专用于解决 H5 唤起 App 这个场景,文档写的也不错,像模像样的,环境判断做的是更加的全面,相比我之前整理的丁香直播跳转流程全面多了,
截取一段代码注释,可以看到作者将巨头们干过的“坏事”具体到日期都一一记录了下来。
结合这个包的环境判断逻辑,个人对唤端原理的理解还有之前调研的一些经验,以「丁香园」App 为例整理出了一张「终极流程图」,为什么取名“终极”,主要是因为再也不想研究这回“麻烦”事了。打算所有应用的唤起策略都向此流程图看齐。
既然社区已经有了轮子,能否直接将轮子复用?通过封装一层配置,将丁香园系应用的配置预先传入,从而做到调用方便(少传几个参数),并且可以按照我们的「终极流程图」策略进行唤醒。
理想很美好,验证的时候第一条用例就翻车了,callapp-lib
直接写死跳转到 App Store 去了,这根本没法打开应用深度的页面,sad!
直接使用不行,那给代码 fork 一份,跳转逻辑改一改是否可以呢?尝试修改了一下,「丁香园」用例完美通过,但换到「丁香医生」就不行了,原因是「丁香无线」对于「丁香医生」的支持并不好(没有微信引导浏览器打开的提示),微信环境下还不如跳转到「应用宝」。
摆在面前有两个选择:
- 所有丁香园系应用往「丁香园」的唤起策略看齐,修改「丁香无线」的实现
- 放弃所有应用跳转策略的统一,支持跳转策略通过配置化的方式自定义
策略统一要与各业务线产品达成共识,且「丁香无线」开发量不好评估,这么一想就觉得好像没有策略统一的必要了,及时调整战略,选择后者。
如何做到支持跳转策略支持配置呢?
思路整理
接连好几天,一看到以下词汇我就浑身难受,思绪混乱。
某天突然来了一些灵感,尝试给他们归了一下类,那一刻,豁然开朗。
为什么唤端这事情这么复杂,“排列组合”一下情况根本就数不过来,要素有三点:
- 跳转方式
- 跳转链接
- 浏览器环境、机型、系统版本等信息决定用哪种跳转方式和跳转链接
有了核心指导思想,用 OOP 设计一些对象:
接下来我们移步项目 readme 来看一看如何使用:
gitlab dxy-evoke-app(必须是丁香园内部网络才可以访问)
开发过程中的一些感悟
1.使用 TypeScript 开发带来最大的不同是思路上的改变,每一个类,每一个方法编写之前,都去思考入参、出参、方法做的事情,代码组织逻辑上清晰了很多。
2.TS 项目中使用路径别名的成本较高
//tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
}
// rollup.config.js
import alias from '@rollup/plugin-alias';
export default {
plugins: [
...,
alias({
entries: [
{
find: '@',
replacement: pathResolve('src'),
},
],
}),
],
};
// jest.config.ts
export default {
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
}
};
用一个工具就得配置一次,tsc --emitDeclarationOnly
生成的 .d.ts
类型文件的路径上还是有 @
,类型声明文件之间的引用有问题。
明明在 tsconfig.json
中已经配置了,为什么不管用呢?阅读官方文档之后才知道,这个属性只做解析,不会去改写编译文件的路径。
社区上有一个解决方案。
总结起来如果路径结构不是特别复杂,路径别名非刚需,还是用相对路径好了,配置起来较为复杂,且不易调试。
3.工具包的导出思路
最早 dxy-evoke-app
对外暴露的是一个 class,class 上只有一个 open
方法,需要这样调用
const instance = new DxyEvokeApp(params)
instance.open('xxx')
使用方法前还需要创建一个实例,有没有更便捷的调用方式?我想到了 Axios,学习了一下源码之后发现默认导出了一个方法 axios
,直接使用 axios()
就是以默认配置调用。在 js 中方法也是对象,对象上挂载了创建实例的方法 create
,通过 axios.create(params)
创建新的实例。
于是我将其思路学习了过来:
import { EvokeAppConfig, DxyEvokeAppStatic } from './types';
import DxyEvokeApp from './core/DxyEvokeApp';
function createInstance(initConfig: EvokeAppConfig = {}): DxyEvokeAppStatic {
const context = new DxyEvokeApp(initConfig);
const func = context.open.bind(context);
return func as DxyEvokeAppStatic;
}
const dxyEvokeApp = createInstance();
dxyEvokeApp.create = createInstance;
export default dxyEvokeApp;
这样以默认配置导出实例的唤起方法 dxyEvokeApp
,通过方法上挂载的 create
方法传入参数创建新的唤起方法。
下一阶段计划
- 跟踪各业务线的接入情况,尽快移除
dxy-get-install
模块 - 持续跟踪
dxy-evoke-app
的使用问题,以 issue 的形式在 gitlab 中进行管理 - 尝试「微信开放标签」的封装,将其以插件的形式集成在 wechat-sdk 中
感谢
- 感谢前端 F2E 松鼠会认可想法并基于资源上的支持
- 感谢「统一的 H5 唤起 App 方案」项目组的同学
- 感谢丁香园、用药助手、丁香医生、丁香妈妈 App 同学给予的帮助
- 感谢丁香医生前端团队,承担了更多的业务需求,让我有精力投入到这件事情上