Quasar CLI with Vite - @quasar/app-vite
警告!Beta 阶段
Quasar SSG 模式目前处于 “beta” 阶段。根据社区反馈,API 在未来可能会有所变动,因此每次升级 “@quasar/app-vite” 时请留意 release notes。
SSG 应用在构建时使用 SSR 渲染管线。相同的原则适用:编写通用代码,仅在渲染期间使用 ssrContext,并保持首次客户端渲染与生成的 HTML 兼容以避免水合错误。
SSG 和 SSR 都发送渲染好的 HTML。SSG 在构建时渲染,而 SSR 对每个请求渲染。因此 SSG 构建没有真实的浏览器请求、cookies、user agent、视口或浏览器存储,除非您通过页面的 ssrContext 故意模拟这些值。
不要在服务端渲染用户相关的标记然后在水合时立即用不同的浏览器相关标记替换。
需要避免的事项
避免使用以下依赖浏览器的值来在客户端水合之前决定初始标记:
- $q.screen(Screen 插件)。改用 Quasar 的窗口宽度相关 CSS 类。
- $q.platform(Platform 插件)。除非您使用 SSG 渲染器 为每个使用到的 $q.platform 属性组合生成 SSG 页面(并在 ssrContext 中填充特定的 req.headers[‘User-Agent’])。
- $q.cookies(Cookies 插件)。渲染器可以使用刻意构造的、非保密的 cookie 值来生成公开变体,但绝不能将真实用户的 cookie 或个性化内容放入静态构建产物中。
- $q.dark(Dark 插件)。除非您生成独立的变体并配置主机提供匹配的文件。
- $q.localStorage(LocalStorage 插件)
- $q.sessionStorage(SessionStorage 插件)
可以使用 Quasar 的 useHydration 组合式函数来延迟依赖浏览器的标记:
<template>
<div>
<template v-if="isHydrated">
<div v-if="$q.screen.md">...</div>
<div v-else-if="$q.platform.ios">...</div>
</template>
</div>
</template>
<script setup>
import { watch } from 'vue'
import { useHydration } from 'quasar'
const { isHydrated } = useHydration()
watch(isHydrated, value => {
if (value) {
// 现在可以访问并使用 $q.screen
// 和 $q.platform 了
}
})
</script>content_paste
案例研究:明暗主题页面
以下高级示例生成明暗两种变体,然后使用 nginx 根据 cookie 选择提供哪个版本:
- 我们将使用一个名为
theme的 cookie,值可以是light或dark。 - 我们将为每个 Vue Router 路由生成两个 SSG 页面,每个主题一个。
- 最后,我们将在部署服务器上使用 Nginx 并适当配置它,根据客户端的
themecookie 来提供正确的 SSG html 文件。
首先,确保安装了 Cookies 和 Dark 插件,并禁用默认的 SSG 404 错误页面(因为我们将有两个 404 页面,每个主题一个):
{
framework: {
plugins: ['Cookies', 'Dark']
},
ssg: {
error404HtmlFilename: false
}
}content_paste
然后编辑 /src/App.vue,在其中用适当的主题初始化 Dark 插件:
import { useSSRContext } from 'vue'
import { Cookies, useQuasar } from 'quasar'
const $q = useQuasar()
if (import.meta.env.QUASAR_SERVER) {
const ssrContext = useSSRContext()
const cookies = Cookies.parseSSR(ssrContext)
const theme = cookies.get('theme')
$q.dark.set(theme === 'dark')
} else {
const theme = Cookies.get('theme')
$q.dark.set(theme === 'dark')
}content_paste
现在可以编辑 /src-ssg/ssg-renderer 文件了:
import { defineSsgGetPages } from '#q-app'
import routes from '@/router/routes'
export const getSsgPages = defineSsgGetPages(
({ parseVueRouterRoutes /*, ctx */ }) => {
// parseVueRouterRoutes 的使用是可选的,它只是一个辅助函数。
const { ssgPages: initialPages } = parseVueRouterRoutes({
routes,
verbose: true
})
const ssgPages = []
// 我们为每个主题生成 2 个 SSG 页面
for (const theme of ['light', 'dark']) {
// 也添加带主题的 404 页面:
ssgPages.push({
route: '/some-bogus-route',
label: `404 ${theme}`,
dir: '', // 放在根目录
filename: `404-${theme}.html`,
ssrContext: {
req: {
headers: {
cookie: `theme=${theme}`
}
}
}
})
// 对于每个 Vue Router 路由,定义一个带主题的页面:
initialPages.forEach(page => {
// 克隆初始定义
const def = structuredClone(page)
// 设置标签以便在错误日志中获得有意义的信息:
def.label = theme
// 定义自定义文件名:
def.filename = `index-${theme}.html`
// 操纵 ssrContext 以在渲染时设置 "theme" cookie:
def.ssrContext = {
req: {
headers: {
cookie: `theme=${theme}`
}
}
}
// 最后将其添加到返回数组中:
ssgPages.push(def)
})
}
return ssgPages
}
)content_paste
在部署服务器上配置 nginx。我们查找 theme cookie,用它来提供对应的 html 文件,同时确保如果 cookie 未设置,则提供默认主题(在此例中为 “light” 主题)。
server {
# ...
location / {
# ...
set $theme "light";
if ($cookie_theme = "dark") {
set $theme "dark";
}
error_page 404 /404-$theme.html;
try_files $uri $uri/ =404;
index index-$theme.html;
}
}content_paste