使用 Quasar CLI,您可以构建 SSR + PWA 终极组合的应用。为了给 SSR 构建启用 PWA,您需要先编辑 /quasar.config 文件:
return {
// ...
ssr: {
/**
* 是否由 PWA 接管客户端,否则为 SPA。
* @default false
*/
pwa?: boolean;
/**
* 使用 SSR+PWA 时,这是 PWA 客户端回退的
* index html 文件名。仅用于生产环境。
*
* 不要使用 index.html 作为名称,否则会导致 SSR 出错!
*
* @default ssg.pwaOfflineHtmlFilename(如已配置),否则 'offline.html'
*/
pwaOfflineHtmlFilename?: string;
/**
* 扩展/配置 Workbox GenerateSW 选项
* 指定将应用在 `pwa > extendPWAGenerateSWOptions()` 之上的
* Workbox 选项。
*
* https://developer.chrome.com/docs/workbox/the-ways-of-workbox/
*
* 可以是异步的。可以直接修改 "config" 参数,
* 或返回一个新对象与默认配置合并。
*/
extendSSRGenerateSWOptions?: (
config: GenerateSWOptions
) => void | GenerateSWOptions | Promise<void | GenerateSWOptions>;
/**
* 扩展/配置 Workbox InjectManifest 选项
* 指定将应用在 `pwa > extendPWAInjectManifestOptions()` 之上的
* Workbox 选项。
*
* https://developer.chrome.com/docs/workbox/the-ways-of-workbox/
*
* 可以是异步的。可以直接修改 "config" 参数,
* 或返回一个新对象与默认配置合并。
*/
extendSSRInjectManifestOptions?: (
config: InjectManifestOptions
) => void | InjectManifestOptions | Promise<void | InjectManifestOptions>;
}
}导航是如何被处理的
新客户端的第一个请求由 web 服务器处理,因此 SSR 提供初始页面内容,同时 service worker 也在此过程中被安装。
从那以后,service worker 会接管。在两种 Workbox 模式的默认配置下,Quasar 会把离线外壳注册为导航回退,它在你的 service worker 中最终会呈现为:
registerRoute(
new NavigationRoute(createHandlerBoundToURL('offline.html'), {
denylist: [/* ... */]
})
)createHandlerBoundToURL() 直接从预缓存(precache)响应,从不接触网络,而 NavigationRoute 会匹配每一个导航请求。换句话说,此后每一次页面加载都会从缓存提供离线外壳,web 服务器根本不会被访问。这正是让应用秒开并能离线工作的原因,也是这里所说的「客户端接管」的含义。
离线外壳是一个客户端渲染的外壳,因此在这些页面加载中:
- 没有服务端渲染的标记,也没有序列化状态,这意味着不会发生 store 水合
- preFetch 特性 钩子会在客户端而非服务端运行
- 任何只存在于服务端的东西(
ssrContext、它的请求和响应对象、你 boot 文件中的服务端部分)都不会参与
这只涉及整页加载。应用内导航早已由客户端的 Vue Router 处理,而你的 API 调用仍会照常走网络。
让服务端渲染每一次导航
如果你希望 SSR web 服务器继续提供导航,而离线外壳只在网络不可用时才介入,那么请退出导航回退,改为把离线外壳用作某个网络策略的回退。
对于 GenerateSW Workbox 模式:
ssr: {
pwa: true,
extendSSRGenerateSWOptions (workboxConfig) {
delete workboxConfig.navigateFallback
delete workboxConfig.navigateFallbackDenylist
workboxConfig.runtimeCaching = [
...(workboxConfig.runtimeCaching || []),
{
urlPattern: ({ request }) => request.mode === 'navigate',
handler: 'NetworkOnly',
options: {
precacheFallback: { fallbackURL: 'offline.html' }
}
}
]
}
}对于 InjectManifest Workbox 模式,则改为编辑你自己的 service worker:
import { NetworkOnly } from 'workbox-strategies'
import { PrecacheFallbackPlugin } from 'workbox-precaching'
// ...replaces the default NavigationRoute:
registerRoute(
new NavigationRoute(
new NetworkOnly({
plugins: [
new PrecacheFallbackPlugin({
fallbackURL: import.meta.env.QUASAR_PWA_FALLBACK_HTML
})
]
}),
{
denylist: [
new RegExp(import.meta.env.QUASAR_PWA_SERVICE_WORKER_REGEX),
/workbox-(.)*\.js$/
]
}
)
)此时导航又重新依赖网络,因此你可能还想启用 Workbox 的 导航预加载(navigationPreload: true),以避免每次导航都要承担 service worker 的启动耗时。
有关 PWA 的更多信息,请前往 PWA 简介 并阅读整个 PWA 指南章节。