为什么捐赠
API 浏览器
升级指南
创建新项目
quasar.config 配置文件
从 Webpack 项目转换
浏览器兼容性
TypeScript 支持
目录结构
命令列表
CSS 预处理器
使用 VueRouter 进行页面路由
懒加载 - 代码分割
资源处理
Boot 文件
预取特性
API 代理
配置 Vite
处理 import.meta.env
使用 Pinia 管理状态
代码检查与格式化
测试与审计
开发移动应用
Ajax 请求
开放开发服务器到公网
联系站长
Quasar CLI with Vite - @quasar/app-vite
SSG 渲染器

警告!Beta 阶段

Quasar SSG 模式目前处于 “beta” 阶段。根据社区反馈,API 在未来可能会有所变动,因此每次升级 “@quasar/app-vite” 时请留意 release notes。

/src-ssg/ssg-renderer 文件告诉 Quasar 在生产构建时渲染哪些路由。它也可以自定义为每个页面资源添加的预加载标签。

WARNING

SSG 渲染器仅在 quasar build -m ssg 时运行。开发模式按需渲染请求的路由,不会调用 getSsgPages()

结构

该文件导出 getSsgPages,可选导出 renderPreloadTag。生成的模板包含两者:

/src-ssg/ssg-renderer

import { defineSsgGetPages, defineSsgRenderPreloadTag } from '#q-app'
import routes from '@/router/routes'

export const getSsgPages = defineSsgGetPages(({ parseVueRouterRoutes /*, ctx */ }) => {
  // parseVueRouterRoutes 的使用是可选的,它只是一个辅助函数。
  const { ssgPages } = parseVueRouterRoutes({ routes, verbose: true })
  return ssgPages
})

const jsRE = /\.js$/
const cssRE = /\.css$/
const woffRE = /\.woff$/
const woff2RE = /\.woff2$/
const gifRE = /\.gif$/
const jpgRE = /\.jpe?g$/
const pngRE = /\.png$/

export const renderPreloadTag = defineSsgRenderPreloadTag(
  (file /* , { ssrContext } */) => {
    if (jsRE.test(file)) {
      return `<link rel="modulepreload" href="${file}" crossorigin>`
    }

    if (cssRE.test(file)) {
      return `<link rel="stylesheet" href="${file}" crossorigin>`
    }

    if (woffRE.test(file)) {
      return `<link rel="preload" href="${file}" as="font" type="font/woff" crossorigin>`
    }

    if (woff2RE.test(file)) {
      return `<link rel="preload" href="${file}" as="font" type="font/woff2" crossorigin>`
    }

    if (gifRE.test(file)) {
      return `<link rel="preload" href="${file}" as="image" type="image/gif" crossorigin>`
    }

    if (jpgRE.test(file)) {
      return `<link rel="preload" href="${file}" as="image" type="image/jpeg" crossorigin>`
    }

    if (pngRE.test(file)) {
      return `<link rel="preload" href="${file}" as="image" type="image/png" crossorigin>`
    }

    return ''
  }
)

定义 SSG 页面

getSsgPages 导出使用 defineSsgGetPages 包装器。它返回一个页面定义数组,可以是同步或异步的。当数组为空时构建会中止。


type SsgGetPagesCallback = (
  params: SsgGetPagesParams
) => SsgPage[] | Promise<SsgPage[]>;

interface SsgGetPagesParams {
  /**
   * Quasar 构建上下文。
   * 与 /quasar.config 文件中的相同。您可以使用它来访问
   * ctx.appPaths(以及其他属性)来解析页面路径,当使用
   * tinyglobby 等工具手动读取文件系统时尤其有用。
   *
   * @type QuasarContext
   */
  readonly ctx: QuasarContext;

  /**
   * Quasar SSG 配置 (quasar.config file > ssg)
   * @type QuasarSsgConfiguration
   */
  readonly quasarConfSsg: QuasarSsgConfiguration;

  /**
   * 内置辅助函数,解析 Vue Router 路由并自动构建待生成的路由列表。
   * 它会忽略重定向、带参数的路由以及 CSR 定义的路由。
   * 如果需要,您需要手动定义并添加这些 SSG 页面。
   *
   * @param {SsgParseVueRouterParams} options - 配置对象。
   * @param {RouteRecordRaw[]} options.routes - 要解析的 Vue Router 路由定义。
   * @param {string} [options.parentPath='/'] - 可选的父路径。
   * @param {string[]} [options.crawlIgnoreRoutes=[]] - 可选的 picomatch 模式,匹配的路由会被省略,但其子路由仍会被遍历。
   * @param {boolean} [options.verbose=false] - 可选标志,启用详细日志。如果为 true,会记录被忽略的带动态参数的路由。
   * @returns {SsgParseVueRouterResult}
   */
  parseVueRouterRoutes: SsgParseVueRouterRoutes;

  /**
   * 使用基于文件名的路由时的辅助函数,返回 Vue Router 自动生成的路由。
   * 仅在 quasar.config > build > filenameBasedRouting 设为 true 时可用。
   *
   * @returns {Promise<RouteRecordRaw[]>} Vue Router 路由数组。
   */
  getFilenameBasedRoutes: SsgGetFilenameBasedRoutes;
}

type SsgParseVueRouterRoutes = (
  params: SsgParseVueRouterParams
) => SsgParseVueRouterResult;

type SsgParseVueRouterParams = {
  /**
   * 要解析的 Vue Router 路由定义。
   */
  routes: RouteRecordRaw[];

  /**
   * 可选的父路径。
   * @default '/'
   */
  parentPath?: string;

  /**
   * 解析过程中要忽略的路由列表(可选)。
   * 可以使用 picomatch 模式来匹配要忽略的路由。
   * 匹配的路由会被省略,但其子路由仍会被遍历并
   * 按模式评估。
   * https://www.npmjs.com/package/picomatch
   *
   * picomatch 模式说明:
   *   "/admin" 仅匹配该精确路由
   *   "/admin/**" 匹配该精确路由及 /admin 的所有子路由
   *   "/admin/*" 仅匹配 /admin 的直接子路由
   *   "/admin/{users,settings}" 匹配 /admin/users 和 /admin/settings 两个精确路由
   *
   * @example ['/dashboard', '/admin/**']
   * @default []
   */
  crawlIgnoreRoutes?: string[];

  /**
   * 动态路由段的可选动态参数列表。
   * 数组中的每个条目应为一个对象,键是动态段的名称,值是该段对应的值。
   *
   * 关于可选路由参数(如 /user/:id?)的注意事项:
   * 对可选参数使用 { [name]: "" }(空字符串作为值)。不要在对象中省略任何可选参数。
   * 示例:{ "/user/:id?": [{ id: "" }] } 会为 /user 路由生成 SSG 页面。
   *
   * @example { "/user/:id": [{ id: 1 }, { id: 2 }] }
   * @example { "/product/:category/:id": [{ category: "electronics", id: 123 }, { category: "books", id: 456 }] }
   * @default {}
   */
  routesDynamicParamsMap?: Record<
    string,
    Array<Record<string, string | number>>
  >;

  /**
   * 可选标志,启用详细日志。
   * 如果为 true,会记录被忽略的带动态参数的路由。
   *
   * @default false
   */
  verbose?: boolean;
};

type SsgParseVueRouterResult = {
  /**
   * 基于解析的 Vue Router 路由生成的 SSG 页面。
   */
  ssgPages: SsgPage[];

  /**
   * 指示解析过程中是否存在被忽略的路由。
   */
  hasIgnoredRoutes: boolean;

  /**
   * 因匹配 crawlIgnoreRoutes 模式而被忽略的 Vue Router 路由列表。
   */
  crawlIgnoredSsgPages: SsgPage[];

  /**
   * 因为是重定向而被忽略的 Vue Router 路由列表。
   */
  ignoredRedirectSsgPages: SsgPage[];

  /**
   * 因具有动态参数而被忽略的 Vue Router 路由列表。
   */
  ignoredDynamicParamSsgPages: SsgPage[];

  /**
   * 因被标记为客户端渲染 (CSR) 而被忽略的 Vue Router 路由列表。
   */
  ignoredCsrSsgPages: SsgPage[];
};

WARNING

定义 SSG 页面的 route 属性时,不要包含 quasar.config > build.publicPath。仅作为 Vue Router 路由使用。

getSsgPages 示例

import { defineSsgGetPages } from '#q-app'
import products from '@/data/products.json'

export const getSsgPages = defineSsgGetPages(() => [
  { route: '/' },
  ...products.map(product => ({
    route: `/products/${product.slug}`,
    label: product.name
  }))
])

关于 404 错误的提示

您的应用通常会有一个 Vue Router 兜底路由:

/src/router/routes 文件

// 使用 Vue Router 捕获 404 的路由示例
{ path: '/:catchAll(.*)*', component: () => import('@/pages/Error404.vue') }

如果页面定义包含一个不存在的路由,Vue Router 会将其解析到这个兜底路由,Quasar 将写入渲染出的 404 标记。这是有效的渲染,所以构建无法推断该路由是否是个错误。

Quasar 会单独生成配置的 SSG 404 错误页面,因此不要为默认的 404.html 添加页面定义。

渲染预加载标签

Quasar 可以为每个生成页面使用的资源注入 <link rel="preload"><link rel="modulepreload"> 标签。renderPreloadTag 接收每个资源 URL 并返回其 HTML 标签。

生成的渲染器处理 JavaScript、CSS、WOFF 字体和常见图片格式。自定义它以省略昂贵的图片预加载或支持其他资源类型。

renderPreloadTag 示例

import { defineSsgRenderPreloadTag } from '#q-app'

const jsRE = /\.js$/
const cssRE = /\.css$/
const woffRE = /\.woff$/
const woff2RE = /\.woff2$/
const gifRE = /\.gif$/
const jpgRE = /\.jpe?g$/
const pngRE = /\.png$/

export const renderPreloadTag = defineSsgRenderPreloadTag(
  (file, { ssrContext }) => {
    if (jsRE.test(file)) {
      return `<link rel="modulepreload" href="${file}" crossorigin>`
    }

    if (cssRE.test(file)) {
      return `<link rel="stylesheet" href="${file}" crossorigin>`
    }

    if (woffRE.test(file)) {
      return `<link rel="preload" href="${file}" as="font" type="font/woff" crossorigin>`
    }

    if (woff2RE.test(file)) {
      return `<link rel="preload" href="${file}" as="font" type="font/woff2" crossorigin>`
    }

    if (gifRE.test(file)) {
      return `<link rel="preload" href="${file}" as="image" type="image/gif" crossorigin>`
    }

    if (jpgRE.test(file)) {
      return `<link rel="preload" href="${file}" as="image" type="image/jpeg" crossorigin>`
    }

    if (pngRE.test(file)) {
      return `<link rel="preload" href="${file}" as="image" type="image/png" crossorigin>`
    }

    return '' // 不需要预加载标签时返回空字符串
  }
)

始终确保根据您的 CORS 设置和资源托管位置正确应用 crossorigin。默认模板假设使用标准的本地托管或标准 CDN 托管。