各位程序猿朋友们,早上好/下午好/晚上好!我是你们的老朋友Bug猎手,今天咱们来聊聊Vue CLI和Workbox这俩好基友是如何联手打造PWA应用的,让你的网站即使在信号不好的地方也能溜得飞起!
PWA:让你的网站像App一样靠谱
首先,咱们得简单回顾一下PWA是啥。简单来说,PWA(Progressive Web App)就是一套让你的Web应用拥有类似原生App体验的技术集合。它具有以下特点:
- 可靠性 (Reliable): 即使在离线或网络状况不佳的情况下也能快速加载。
- 快速 (Fast): 响应迅速,提供流畅的用户体验。
- 吸引人 (Engaging): 能够像原生应用一样安装到设备主屏幕,并接收推送通知。
Workbox就是谷歌爸爸提供的一套工具,专门用来简化PWA的开发流程,尤其是在Service Worker的编写和管理方面。
Vue CLI:PWA的官方推荐姿势
Vue CLI 绝对是Vue开发者的福音,它能帮你快速搭建项目框架,集成各种工具,当然也包括PWA。
第一步:创建Vue项目并启用PWA
如果你还没有Vue项目,可以使用Vue CLI创建一个:
vue create my-pwa-app
在项目创建过程中,CLI会问你是否要启用PWA支持。选择手动配置 (Manually select features) ,然后选中 "Progressive Web App (PWA) Support" 这一项。
第二步:Workbox的自动配置
选择了PWA支持后,Vue CLI会自动帮你安装并配置workbox-webpack-plugin
。 它会做这些事情:
- 安装
@vue/cli-plugin-pwa
插件。 - 在
vue.config.js
中配置 Workbox 插件。 - 创建一个
registerServiceWorker.js
文件,用于注册Service Worker。 - 在
public
目录下生成manifest.json
文件,用于描述你的PWA应用。
第三步:vue.config.js
中的Workbox配置
打开 vue.config.js
文件,你会发现里面已经有了PWA相关的配置:
module.exports = {
// ... 其他配置
pwa: {
name: 'My PWA App', // 应用名称
themeColor: '#4DBA87', // 主题颜色
msTileColor: '#000000', // Windows磁贴颜色
appleMobileWebAppCapable: 'yes', // 是否启用WebApp全屏模式
appleMobileWebAppStatusBarStyle: 'black', // 状态栏样式
// configure the workbox plugin
workboxPluginMode: 'GenerateSW', // or 'InjectManifest'
workboxOptions: {
// 这些选项将直接传递给 workbox-webpack-plugin
skipWaiting: true,
clientsClaim: true,
// ... 其他 Workbox 配置
}
}
}
这里有几个重要的配置项:
name
: PWA 应用的名称,会显示在添加到主屏幕后的应用图标下方。themeColor
: PWA 应用的主题颜色,会影响浏览器工具栏的颜色。workboxPluginMode
: 指定 Workbox 插件的运行模式。GenerateSW
(默认): Workbox 会自动生成一个完整的 Service Worker 文件(service-worker.js
)。这是最简单的方式,但灵活性较低。InjectManifest
: 你需要自己编写 Service Worker 文件,Workbox 会将你的配置注入到这个文件中。这种方式更灵活,但需要更多的手动配置。
workboxOptions
: 一个对象,包含了所有可以传递给workbox-webpack-plugin
的选项。
第四步:registerServiceWorker.js
文件
这个文件负责注册 Service Worker。通常情况下,你不需要修改它,因为它已经包含了基本的注册逻辑:
/* eslint-disable no-console */
import { register } from 'register-service-worker'
if (process.env.NODE_ENV === 'production') {
register(`${process.env.BASE_URL}service-worker.js`, {
ready () {
console.log(
'App is being served from cache by a service worker.n' +
'For more details, visit https://goo.gl/AFskqB'
)
},
registered () {
console.log('Service worker has been registered.')
},
cached () {
console.log('Content has been cached for offline use.')
},
updatefound () {
console.log('New content is downloading.')
},
updated () {
console.log('New content is available; please refresh.')
},
offline () {
console.log('No internet connection found. App is running in offline mode.')
},
error (error) {
console.error('Error during service worker registration:', error)
}
})
}
第五步:manifest.json
文件
这个文件描述了你的 PWA 应用的元数据,比如名称、图标、启动 URL 等。它位于 public
目录下,内容类似这样:
{
"name": "My PWA App",
"short_name": "PWA App",
"icons": [
{
"src": "./img/icons/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "./img/icons/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#4DBA87",
"background_color": "#000000"
}
name
: 应用的完整名称。short_name
: 应用的简短名称,用于主屏幕上。icons
: 应用图标的数组,不同尺寸的图标用于不同的设备。务必提供不同尺寸的图标,否则PWA无法正常添加到主屏幕。start_url
: 应用启动时加载的 URL。display
: 指定应用的显示模式。standalone
: 应用会以独立的窗口运行,就像原生 App 一样。fullscreen
: 应用会以全屏模式运行。minimal-ui
: 应用会以最小化的 UI 运行,只显示浏览器的后退、前进和刷新按钮。browser
: 应用会在标准的浏览器窗口中运行。
theme_color
: 应用的颜色主题。background_color
: 应用在启动时显示的背景颜色。
第六步:打包并测试你的PWA
运行以下命令打包你的应用:
npm run build
打包完成后,你会得到一个 dist
目录,包含了你的 PWA 应用的所有文件。
要测试你的 PWA,你需要一个 HTTP 服务器。你可以使用 serve
命令:
npm install -g serve
serve -s dist
然后在浏览器中打开 http://localhost:5000
(或者 serve
命令输出的地址)。
打开 Chrome 开发者工具,切换到 "Application" 选项卡,在 "Manifest" 面板中查看你的 manifest.json
文件是否正确加载。
在 "Service Workers" 面板中查看你的 Service Worker 是否已经注册并激活。
模拟离线状态:在 "Application" -> "Service Workers" 面板中,勾选 "Offline" 复选框。刷新页面,如果你的 PWA 应用能够正常显示,说明离线功能已经生效!
Workbox的两种模式深度解析:GenerateSW
vs InjectManifest
咱们前面提到了 workboxPluginMode
这个配置项,它决定了 Workbox 的运行模式。现在咱们来深入了解一下这两种模式的区别:
1. GenerateSW
模式 (自动挡)
- 优点:
- 简单易用,不需要手动编写 Service Worker 代码。
- Workbox 会自动处理所有静态资源的缓存,并生成一个完整的 Service Worker 文件。
- 缺点:
- 灵活性较低,无法自定义 Service Worker 的行为。
- 对于复杂的缓存策略,可能需要编写自定义代码来扩展 Workbox 的功能。
- 适用场景:
- 对于简单的 PWA 应用,或者不需要复杂的缓存策略的应用,
GenerateSW
模式是一个不错的选择。
- 对于简单的 PWA 应用,或者不需要复杂的缓存策略的应用,
如何使用 GenerateSW
模式?
-
在
vue.config.js
中设置workboxPluginMode: 'GenerateSW'
。 -
在
workboxOptions
中配置缓存策略和其他选项。例如,你可以使用runtimeCaching
选项来配置运行时缓存:module.exports = { pwa: { workboxPluginMode: 'GenerateSW', workboxOptions: { skipWaiting: true, clientsClaim: true, runtimeCaching: [ { urlPattern: /^https://fonts.(googleapis|gstatic).com/, handler: 'CacheFirst', options: { cacheName: 'google-fonts-cache', expiration: { maxAgeSeconds: 60 * 60 * 24 * 365, // 缓存一年 maxEntries: 30 }, cacheableResponse: { statuses: [0, 200] // 缓存状态码为 0 和 200 的响应 } } }, { urlPattern: /.(png|jpg|jpeg|svg|gif)$/, handler: 'CacheFirst', options: { cacheName: 'images-cache', expiration: { maxEntries: 60, maxAgeSeconds: 30 * 24 * 60 * 60 // 缓存 30 天 } } } ] } } }
这个例子中,我们配置了两个运行时缓存策略:
- 第一个策略缓存 Google Fonts 的字体文件,使用
CacheFirst
策略,缓存一年。 - 第二个策略缓存图片文件,使用
CacheFirst
策略,缓存 30 天。
- 第一个策略缓存 Google Fonts 的字体文件,使用
2. InjectManifest
模式 (手动挡)
- 优点:
- 灵活性高,可以完全自定义 Service Worker 的行为。
- 可以编写复杂的缓存策略,并使用 Workbox 提供的各种 API 来实现更高级的功能。
- 缺点:
- 需要手动编写 Service Worker 代码,学习成本较高。
- 需要自己处理静态资源的缓存,并确保 Service Worker 的正确运行。
- 适用场景:
- 对于需要高度自定义的 PWA 应用,或者需要使用 Workbox 的高级功能的应用,
InjectManifest
模式是更好的选择。
- 对于需要高度自定义的 PWA 应用,或者需要使用 Workbox 的高级功能的应用,
如何使用 InjectManifest
模式?
-
在
vue.config.js
中设置workboxPluginMode: 'InjectManifest'
。 -
创建一个 Service Worker 文件,例如
src/service-worker.js
。 -
在
vue.config.js
的workboxOptions
中指定 Service Worker 文件的路径:module.exports = { pwa: { workboxPluginMode: 'InjectManifest', workboxOptions: { swSrc: 'src/service-worker.js' // 指定 Service Worker 文件的路径 } } }
-
在
src/service-worker.js
中编写你的 Service Worker 代码。例如:import { clientsClaim } from 'workbox-core'; import { precacheAndRoute } from 'workbox-precaching'; import { registerRoute } from 'workbox-routing'; import { CacheFirst, NetworkFirst, StaleWhileRevalidate } from 'workbox-strategies'; import { ExpirationPlugin } from 'workbox-expiration'; import { CacheableResponsePlugin } from 'workbox-cacheable-response'; clientsClaim(); // Precache all of the assets generated by your build process. // Their URLs are injected into the manifest variable below. // This variable must be declared and exported in your service-worker.js file. precacheAndRoute(self.__WB_MANIFEST); // Cache the Google Fonts stylesheets with a cache first strategy. registerRoute( /^https://fonts.googleapis.com/, new CacheFirst({ cacheName: 'google-fonts-stylesheets', plugins: [ new CacheableResponsePlugin({ statuses: ['0', 200], }), ], }) ); // Cache the underlying font files with a cache-first strategy for 1 year. registerRoute( /^https://fonts.gstatic.com/, new CacheFirst({ cacheName: 'google-fonts-webfonts', plugins: [ new CacheableResponsePlugin({ statuses: ['0', 200], }), new ExpirationPlugin({ maxAgeSeconds: 60 * 60 * 24 * 365, maxEntries: 30, }), ], }) ); // Cache images with a cache-first strategy for 30 days. registerRoute( /.(?:png|jpg|jpeg|svg|gif)$/, new CacheFirst({ cacheName: 'images', plugins: [ new CacheableResponsePlugin({ statuses: ['0', 200], }), new ExpirationPlugin({ maxEntries: 60, maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days }), ], }) ); // An example runtime caching route for dynamic content. registerRoute( ({ url }) => url.pathname.startsWith('/api/'), new NetworkFirst({ cacheName: 'api-cache', plugins: [ new CacheableResponsePlugin({ statuses: [0, 200], }), new ExpirationPlugin({ maxEntries: 50, maxAgeSeconds: 5 * 60, // 5 minutes }), ], }) ); // This allows the web app to trigger skipWaiting via // registration.waiting.postMessage({type: 'SKIP_WAITING'}) self.addEventListener('message', (event) => { if (event.data && event.data.type === 'SKIP_WAITING') { self.skipWaiting(); } });
clientsClaim()
: 让 Service Worker 立即控制所有客户端。precacheAndRoute(self.__WB_MANIFEST)
: 预缓存所有静态资源,self.__WB_MANIFEST
是 Workbox 注入的变量,包含了所有需要缓存的资源列表。registerRoute()
: 注册路由,用于处理不同 URL 的缓存策略。CacheFirst
,NetworkFirst
,StaleWhileRevalidate
: Workbox 提供的缓存策略。ExpirationPlugin
: 用于设置缓存的过期时间。CacheableResponsePlugin
: 用于设置哪些响应需要缓存。
Workbox常用缓存策略
Workbox 提供了多种缓存策略,可以根据不同的场景选择合适的策略:
策略 | 描述 | 适用场景 |
---|---|---|
CacheFirst |
首先尝试从缓存中获取资源,如果缓存中不存在,则从网络获取,并将响应缓存起来。 | 静态资源,例如图片、字体、CSS、JavaScript 文件。 |
NetworkFirst |
首先尝试从网络获取资源,如果网络请求失败,则从缓存中获取。 | 动态内容,例如 API 请求。 |
StaleWhileRevalidate |
首先从缓存中获取资源,同时在后台从网络获取最新的资源,并在下次请求时使用最新的资源。 | 对实时性要求不高,但需要快速响应的内容。 |
NetworkOnly |
仅从网络获取资源。 | 必须从网络获取的资源。 |
CacheOnly |
仅从缓存获取资源。 | 离线状态下需要访问的资源。 |
常见问题与解决方案
-
PWA 应用无法添加到主屏幕?
- 确保你的
manifest.json
文件正确配置,并且提供了不同尺寸的图标。 - 确保你的网站使用了 HTTPS 协议。
- 确保你的网站已经注册了 Service Worker。
- 确保你的
-
Service Worker 没有生效?
- 检查你的
registerServiceWorker.js
文件是否正确注册了 Service Worker。 - 检查你的 Service Worker 文件是否有语法错误。
- 在 Chrome 开发者工具的 "Application" -> "Service Workers" 面板中查看 Service Worker 的状态。
- 检查你的
-
如何更新 Service Worker?
- 当你的网站更新时,浏览器会自动下载新的 Service Worker 文件。
- 新的 Service Worker 会在后台安装,并在下次页面加载时激活。
-
如果需要立即激活新的 Service Worker,可以在 Service Worker 文件中添加以下代码:
self.addEventListener('message', (event) => { if (event.data && event.data.type === 'SKIP_WAITING') { self.skipWaiting(); } });
然后在你的应用中发送
SKIP_WAITING
消息:if (navigator.serviceWorker.controller) { navigator.serviceWorker.controller.postMessage({ type: 'SKIP_WAITING' }); }
-
如何调试 Service Worker?
- 使用 Chrome 开发者工具的 "Application" -> "Service Workers" 面板。
- 在 Service Worker 代码中使用
console.log()
打印日志。 - 使用 Workbox 提供的调试工具。
总结
Vue CLI 结合 Workbox 可以让你轻松地创建 PWA 应用,让你的网站拥有更好的用户体验。选择 GenerateSW
模式可以快速上手,而 InjectManifest
模式则提供了更高的灵活性。根据你的需求选择合适的模式,并学习 Workbox 提供的各种 API,你就可以打造出功能强大的 PWA 应用。
希望今天的讲座对你有所帮助!如果有什么问题,欢迎在评论区留言,我会尽力解答。祝大家早日摆脱Bug,代码飞起!