把网站添加到桌面,「爱奇艺网页应用」是如何实现的
# 背景
希望网站提供一个「添加网页应用」功能,点击安装后,自动为用户创建一个网站的桌面快捷方式。
下次用户直接从桌面快捷方式,即可访问我们的网站
# 方案
# 1. Chrome App
在线安装(由于网络原因不太可行,而且如今已经没有入口了) or 离线下载
举几个熟悉的 Chrome 应用:微信、Postman 。可以理解为一个独立的新应用了,和网站本身没有任何关系,因此可拓展性也较高。
曾经较为流行,由于各种原因,chrome 将不再支持(目前只支持更新,不再上新),所以该方案放弃
详见 https://blog.chromium.org/2020/01/moving-forward-from-chrome-apps.html
# 2. Progressive Web App (PWA)
即 渐进式 Web 应用 (opens new window),这不是一个单独的技术,而是多种技术的组合,包括
- Web app manifests (opens new window): 通过 JSON 提供应用程序相关信息,目的是将应用程序安装到设备桌面
- Service Worker (opens new window): 充当浏览器与服务端间的网络代理,提供离线访问能力
- Push API (opens new window): 从服务器向客户端推送消息,需要借助 service worker
- Notifications API (opens new window): 允许网页或应用程序在系统级别发送在页面外部显示的通知
- Add to Home Screen (opens new window): 将应用程序安装到设备桌面的关键,需要浏览器支持 beforeinstallprompt 事件
不要被上述技术栈吓到。。如果只是为了实现 「把网站添加到桌面」功能,仅需要 Web app manifests
, Add to Home Screen
, Service Worker
. 5分钟就可以开发好
# 功能实现
首先需要说明的是,我们的网站必须运行在 https 环境。所以开发测试的时候可以用 github.io
白嫖
大体流程如下:
- 编写一个简单的 HTML 页面
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>A2HS demo</title>
<style>
.add-button {
position: absolute;
top: 1px;
left: 1px;
}
</style>
<script src="index.js" defer></script>
<link rel="manifest" href="manifest.webmanifest">
</head>
<body>
<div>测试文本</div>
<button class="add-button">添加到桌面</button>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- 编写一份
Web app manifests
JSON(后面会详细讲解各个字段) ,命名为manifest.webmanifest
? (opens new window)
{
"background_color": "purple",
"display": "fullscreen",
"icons": [
{
"src": "icon/fox-icon.png",
"sizes": "192x192",
"type": "image/png"
}
],
"name": "xx快捷版",
"short_name": "快",
"start_url": "."
}
2
3
4
5
6
7
8
9
10
11
12
13
14
- 注册安装事件
index.js
let deferredPrompt;
// 默认不展示按钮,仅支持 「Add to Home Screen」 功能才展现
const addBtn = document.querySelector('.add-button');
addBtn.style.display = 'none';
// 规定必须注册 serviceWorker 才能使用 Add to Home Screen,
// 且需要监听 install 和 fetch 事件,可以不处理
if('serviceWorker' in navigator) {
navigator.serviceWorker
.register('./sw.js')
.then(function() { console.log('Service Worker Registered'); });
}
// 仅浏览器支持且未安装该应用,以下事件才会触发
window.addEventListener('beforeinstallprompt', (e) => {
// Chrome 67 及之前版本,会自动展现安装的 prompt
// 为了版本统一及用户体验,我们禁止自动展现 prompt
e.preventDefault();
// 存放事件用于后续触发
deferredPrompt = e;
// 展现按钮
addBtn.style.display = 'block';
addBtn.addEventListener('click', (e) => {
// hide our user interface that shows our A2HS button
addBtn.style.display = 'none';
// 展现安装的 prompt
deferredPrompt.prompt();
// 等待用户对 prompt 进行操作
// 如果用户从地址栏或其他浏览器组件安装了PWA,则以下代码将不起作用
deferredPrompt.userChoice.then((choiceResult) => {
if (choiceResult.outcome === 'accepted') {
console.log('点击添加');
} else {
console.log('取消添加');
}
deferredPrompt = null;
});
});
});
// 无论以何种方式安装 PWA 该事件都会触发
// 因此这里可以用来做埋点
window.addEventListener('appinstalled', (evt) => {
console.log('应用安装');
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
- 编写 sw 文件
sw.js
self.addEventListener('install', function(e) {
console.log('install success')
});
self.addEventListener('fetch', function(e) {
console.log(e.request.url);
});
2
3
4
5
6
7
- 最后,准备一个 icon ,见
manifest.webmanifest
中的 icons 配置
之后,就可以体验这个功能了
# 在线示例
https://fe-examples.github.io/pwa-examples/a2hs/
如果浏览器支持 ,页面左上角将展现 「添加到桌面」按钮
欢迎试用,并把操作系统、浏览器型号发在评论区,看看兼容性如何~
# Web app manifests 相关配置
这里只举常用的,更多详见 WebAppManifest (opens new window)
- background_color: 应用启动时资源未加载时展示的背景色。仅用于改善加载时的用户体验,不影响后续网站背景。
- theme_color: 主题色,相当于显式的设置
<meta name="theme-color" content="颜色值">
,将会影响浏览器外观颜色的展现。 - display: 指定如何显示应用。有以下几种取值,在不支持的情况下按从上往下顺序 fallback
- fullscreen: 占用所有可用显示区域,无任何浏览器 UI 元素。不影响 Fullscreen API
- standalone: 类似本机应用程序。排除地址栏,但是可能包含状态栏和系统后退按钮
- minimal-ui: 相比
standalone
增加了导航控件(后退,前进,刷新)等 UI 元素 - browser: 等同于打开标签页。需要注意的是, browser 是兜底方式且仅部分浏览器支持,如果浏览器还不支持,整份配置将无效
Macos chrome 下测试 2 和 1 显示一致,不支持 4
- icons: 指定在不同位置时采用的图标,主要是根据 sizes 字段来判断。相比移动端 APP 的 icon ,PWA 多了桌面位置、Chrome 应用入口位置。至少指定一个 icon
- name/short_name: 前者为完整的应用名称,后者为当没有足够空间显示全名时才展示。最好两者都提供
- start_url: 应用启动时所加载的 URL 。其值为相对于 manifest 的 url 的相对路径
例如 manifest URL 为
https://www.iqiyi.com/manifest.json
; start_url 为./index.html
,则启动地址为https://www.iqiyi.com/index.html
。而 start_url 为.
启动地址就是https://www.iqiyi.com/
还有更多的骚操作,比如把 uuid 或者 launcher 方式带在start_url
后面(./?launcher=homescreen&uuid=xxxx
),有助于用户分析
singleton
minimal-ui
# 根据不同的启动方式应用不同样式
比如说,PWA 背景色应用红色,普通 Tab 应用白色,该如何实现呢?
可以采用 CSS 的媒体查询
@media all and (display-mode: standalone) {
body {
background-color: red;
}
}
2
3
4
5
或者脚本检测
window.addEventListener('load', () => {
if (navigator.standalone) {
console.log('Launched: Installed (iOS)');
} else if (matchMedia('(display-mode: standalone)').matches) {
console.log('Launched: Installed');
} else {
console.log('Launched: Browser Tab');
}
});
2
3
4
5
6
7
8
9
⚠️ 注意:iOS Safari 不支持 matchMedia ,但是其可以通过 navigator.standalone
返回的 true 表明自己以独立模式运行
# 浏览器兼容性
可以看到,我们仅用到了 PWA 体系中的 Web app manifests
, Add to Home Screen
和 Service Worker
,因此该功能的浏览器兼容性取决于这几个的短板
# Service Worker
# Add to Home Screen
待测试,本机(Macos chrome 81 支持)
# Web app manifests
待测试,本机(Macos chrome 81 支持)
# 结论
(兼容性列表欢迎大家一起维护~) ...
# 题外话 - 自己动手丰衣足食
对于普通的 Chrome 用户,如果自己喜欢的网站没有实现上述功能,自己如何把网站添加到桌面呢?
右上角选择「更多工具」-「创建快捷方式」- 「确定」
如果选择新窗口打开,新开窗口将不带导航栏、工具栏等,类似于
display=standalone
,否则就是普通的打开标签页display=browser
可以看到桌面就存在一个该网站的图标了~ 点击就可以打开该网站