渐进式 web 应用程序
渐进式 web 应用程序(PWA)由一系列技术和模式组成,主要用于改善用户体验,帮助创建更可靠和可用的应用程序。尤其是移动用户,他们会发现应用程序能更好的集成到他们的设备中,就跟本地安装的应用程序一样。
渐进式 web 应用程序主要由两种技术组成:Service Worker 和 Manifest。Dojo 的构建命令通过 .dojorc
的 pwa
对象支持这两种技术。
Manifest
Manifest 在一个 JSON 文件中描述一个应用程序,并提供了一些详细信息,因此可以直接从万维网安装到设备的主屏幕上。
.dojorc
{
"build-app": {
"pwa": {
"manifest": {
"name": "Todo MVC",
"description": "A simple to-do application created with Dojo",
"icons": [
{ "src": "./favicon-16x16.png", "sizes": "16x16", "type": "image/png" },
{ "src": "./favicon-32x32.png", "sizes": "32x32", "type": "image/png" },
{ "src": "./favicon-48x48.png", "sizes": "48x48", "type": "image/png" },
{ "src": "./favicon-256x256.png", "sizes": "256x256", "type": "image/png" }
]
}
}
}
}
当提供了 manifest 信息时,dojo build
将在应用程序的 index.html
中注入必需的 <meta>
标签。
mobile-web-app-capable="yes"
: 告知 Android 上的 Chrome 可以将此应用程序添加到用户的主界面上。apple-mobile-web-app-capable="yes"
: 告知 iOS 设备可以将此应用程序添加到用户的主界面上。apple-mobile-web-app-status-bar-style="default"
: 告知 iOS 设备,状态栏使用默认外观。apple-touch-icon="{{icon}}"
: 相当于 Manifest 中的 icons,因为 iOS 当前没有从 Manifest 中读取 icons,所以需要为 icons 数组中每张图片单独注入一个 meta 标签。
Service worker
Service worder 是一种 web worker,能够拦截网络请求、缓存和提供资源。Dojo 的 build 命令能够自动构建功能全面的 service worker,它会在启动时激活,然后使用配置文件完成预缓存和自定义路由处理。
例如,我们编写一个配置文件来创建一个简单的 service worker,它会缓存除了 admin 包之外的所有应用程序包,也会缓存应用程序最近访问的图像和文章。
.dojorc
{
"build-app": {
"pwa": {
"serviceWorker": {
"cachePrefix": "my-app",
"excludeBundles": ["admin"],
"routes": [
{
"urlPattern": ".*\\.(png|jpg|gif|svg)",
"strategy": "cacheFirst",
"cacheName": "my-app-images",
"expiration": { "maxEntries": 10, "maxAgeSeconds": 604800 }
},
{
"urlPattern": "http://my-app-url.com/api/articles",
"strategy": "cacheFirst",
"expiration": { "maxEntries": 25, "maxAgeSeconds": 86400 }
}
]
}
}
}
}
ServiceWorker 配置
在底层,@dojo/webpack-contrib
中的 ServicerWorkerPlugin
用于生成 service worker,它的所有选项都是有效的 pwa.serviceWorker
属性。
属性 | 类型 | 可选 | 描述 |
---|---|---|---|
bundles | string[] | 是 | 需要进行预缓存的一组包。默认是所有包。 |
cachePrefix | string | 是 | 在运行时进行预缓存使用的前缀。 |
clientsClaim | boolean | 是 | Service worker 是否要在开始激活时控制客户端。默认为 false 。 |
excludeBundles | string[] | 是 | 要从预缓存中排除的一组包。默认为 [] 。 |
importScripts | string[] | 是 | 需要在 service worker 中加载的一组脚本的路径。 |
precache | object | 是 | 描述预缓存配置选项的对象(见下文)。 |
routes | object[] | 是 | 一组描述要在运行时缓存的配置对象(见下文)。 |
skipWaiting | boolean | 是 | Service worker 是否要跳过“等待”生命周期。 |
预缓存
precache
选项使用以下选项控制预缓存行为:
属性 | 类型 | 可选 | 描述 |
---|---|---|---|
baseDir | string | 是 | 匹配 include 时使用的根目录。 |
ignore | string[] | 是 | 一组通配符模式的字符串,当生成预缓存项时用于匹配需要忽略的文件。默认为 [ ‘node_modules/*/‘ ] 。 |
include | string or string[] | 是 | 一个或者一组通配符模式的字符串,用于匹配 precache 应该包含的文件。默认是构建管道中的所有文件。 |
index | string | 是 | 如果请求以 / 结尾的 URL 失败,则应该查找的 index 文件名。默认为 ‘index.html’ 。 |
maxCacheSize | number | 是 | 往预缓存中添加的每一个文件不应超过的最大字节数。默认为 2097152 (2 MB)。 |
strict | boolean | 是 | 如果为 true ,则 include 模式匹配到一个不存在的文件夹时,构建就会失败。默认为 true 。 |
symlinks | boolean | 是 | 当生成预缓存时是否允许软连接(symlinks)。默认为 true 。 |
运行时缓存
除了预缓存之外,还可以为特定路由提供缓存策略,以确定它们是否可以缓存以及如何缓存。routes
选项是一组包含以下属性的对象:
属性 | 类型 | 可选 | 描述 |
---|---|---|---|
urlPattern | string | 否 | 用于匹配特定路由的模式字符串(会被转换为正则表达式)。 |
strategy | string | 否 | 缓存策略(见下文)。 |
options | object | 是 | 一个描述附加选项的对象。每个选项的详情如下。 |
cacheName | string | 是 | 路由使用的缓存名称。注意 cachePrefix 不会 添加到缓存名前。默认为主运行时缓存(${cachePrefix}-runtime-${domain} )。 |
cacheableResponse | object | 是 | 使用 HTTP 状态码或者报头(Header)信息来决定是否可以缓存响应的内容。此对象有两个可选属性:statuses 和 headers 。statuses 是一组对缓存生效的状态码。headers 是一组 HTTP 的 header 和 value 键值对;至少要与一个报头匹配,响应才会被视为有效。当 strategy 的值是 ‘cacheFirst’ 时,默认为 { statuses: [ 200 ] } ;当 strategy 的值为 networkFirst 或者 staleWhileRevalidate 时,默认为 { statuses: [0, 200] } |
expiration | object | 是 | 控制如何让缓存失效。此对象有两个可选属性。maxEntries 是任何时间可以缓存的响应个数。一旦超过此最大值,就会删除最旧的条目。maxAgeSeconds 是一个响应能缓存的最长时间(以秒为单位),超过此时长就会被删除。 |
networkTimeoutSeconds | number | 是 | 与 networkFirst 策略一起使用,指定当网络请求的响应多久没有返回时就从缓存中获取资源,单位为秒。 |
目前支持四种路由策略:
networkFirst
尝试通过网络加载资源,如果请求失败或超时才从缓存中获取资源。对于频繁更改或者可能频繁更改(即没有版本控制)的资源,这是一个很有用的策略。cacheFirst
优先从缓存中加载资源,如果缓存中不存在,则通过网络获取。这对于很少更改或者能缓存很长一段时间的资源(受版本控制的资源)来说是最好的策略。networkOnly
强制始终通过网络获取资源,对于无需离线处理的资源是很有用的策略。staleWhileRevalidate
同时从缓存和网络中请求资源。网络成功响应后都会更新缓存。此策略最适用于不需要持续更新的资源,比如用户信息。但是,当获取第三方资源时没有发送 CORS 报头,就无法读取响应的内容或验证状态码。因此,可能会缓存错误的响应。在这种情况下,networkFirst
策略可能更适合。