为什么捐赠
API 资源管理器
升级指南
新增!
quasar.config 文件
将项目转换为使用 Vite 的 CLI
浏览器兼容性
支持 TypeScript
目录结构
命令列表
CSS 预处理器
路由
延迟加载 - 代码分割
处理资源
启动文件
预取功能
API 代理
处理 Vite
处理 process.env
使用 Pinia 进行状态管理
使用 Vuex 进行状态管理
代码风格检查
测试与审计
开发移动应用
Ajax 请求
将开发服务器开放到公网
使用 Vite 的 Quasar CLI - @quasar/app-vite
SSR 中间件

SSR 中间件文件只有一个特殊用途:它们为运行 SSR 应用的 Nodejs 服务器准备额外的功能(与 Expressjs 兼容的中介软件)。

通过 SSR 中间件文件,可以将中间件逻辑拆分成自包含、易于维护的文件。还可以轻松禁用任何 SSR 中间件文件,甚至可以通过 /quasar.config 文件配置来上下文确定哪些 SSR 中间件文件进入构建。

提示

对于更高级的用法,您需要熟悉 Expressjs API

警告

您至少需要一个 SSR 中间件文件来处理使用 Vue 渲染页面(应将其放置在中间件列表的最后)。当将 SSR 模式添加到您的 Quasar CLI 项目时,它将被脚手架到 src-ssr/middlewares/render.js 中。

中间件文件结构

SSR 中间件文件是一个简单的 JavaScript 文件,它导出一个函数。Quasar 然后在准备 Nodejs 服务器 (Expressjs) 应用程序时调用导出的函数,并额外传递一个对象作为参数(将在下一节中详细介绍)。

// import something here

export default ({ app, port, resolve, publicPath, folders, render, serve }) => {
  // something to do with the server "app"
}

SSR 中间件文件也可以是异步的

// import something here

export default async ({ app, port, resolve, publicPath, folders, render, serve }) => {
  // something to do with the server "app"
  await something()
}

您可以使用 ssrMiddleware 辅助函数包装返回的函数,以获得更好的 IDE 自动完成体验(通过 Typescript)

import { ssrMiddleware } from 'quasar/wrappers'

export default ssrMiddleware(async ({ app, port, resolve, publicPath, folders, render, serve }) => {
  // something to do
  await something()
})

请注意,我们正在使用 ES6 解构赋值。仅分配您实际需要/使用的内容。

中间件对象参数

我们在这里指的是 SSR 中间件文件默认导出函数的参数对象。

export default ({ app, port, resolve, publicPath, folders, render, serve }) => {

对象详情

{
  app, // Node.js app instance
  port, // Nodej.js webserver configured port
  resolve: {
    urlPath(path)
    root(arg1, arg2),
    public(arg1, arg2)
  },
  publicPath, // String
  folders: {
    root,     // String
    public    // String
  },
  render(ssrContext),
  serve: {
    static(path, opts),
    error({ err, req, res })
  }
}

app

这是 Node.js 应用程序实例。“核心” 任何中间件,因为您将使用它来配置 Web 服务器。

port

Node.js Web 服务器的配置端口。

resolve

属性名称描述
urlPath(path)每当您定义路由(使用 app.use()、app.get()、app.post() 等)时,都应使用 resolve.urlPath() 方法,以便您还可以考虑配置的 publicPath(quasar.config 文件 > build > publicPath)。
root(path1[, path2, ...pathN])将文件夹路径解析到根目录(开发中的项目根目录和生产中的分发文件根目录)。在后台,它执行 path.join()
public(path1[, path2, ...pathN])将文件夹路径解析到“public”文件夹。在后台,它执行 path.join()

publicPath

配置的 quasar.config 文件 > build > publicPath

folders

folders 有时是必要的,因为根文件夹和 public 文件夹的确切路径在生产构建中与开发构建中有所不同。因此,通过使用 folders,您无需考虑这一点。

属性名称描述
root到根目录的完整路径(开发中的项目根目录和生产中的分发文件根目录)。
public到“public”文件夹的完整路径。

render

  • 语法:<Promise(String)> render(ssrContext)
  • 描述:使用 Vue 和 Vue Router 渲染请求的 URL 路径。返回渲染后的 HTML 字符串以返回给客户端。

serve

serve.static()

  • 语法:<middlewareFn> serve.static(pathFromPublicFolder, opts)

  • 描述:它本质上是 express.static() 的包装器,并进行了一些方便的调整

    • pathFromPublicFolder 是一个解析到“public”文件夹的路径。
    • optsexpress.static() 的相同。
    • opts.maxAge 默认使用,并考虑 quasar.config 文件 > ssr > maxAge 配置;这设置了相应文件在浏览器缓存中的生存时间。
    serve.static('my-file.json')
    
    // is equivalent to:
    
    express.static(resolve.public('my-file.json'), {
      maxAge: ... // quasar.config file > ssr > maxAge
    })

serve.error()

  • 语法:<void> serve.error({ err, req, res })
  • 描述:显示大量有用的调试信息(包括堆栈跟踪)。
  • 它仅在开发中可用,**在生产中不可用**。

SSR 中间件的使用

第一步始终是使用 Quasar CLI 生成一个新的 SSR 中间件文件

$ quasar new ssrmiddleware <name>

其中 <name> 应替换为您适合的 SSR 中间件文件名。

此命令创建一个新文件:/src-ssr/middlewares/<name>.js,内容如下

// import something here

// "async" is optional!
// remove it if you don't need it
export default async ({ app, port, resolveUrlPath, publicPath, folders, render, serve }) => {
  // something to do with the server "app"
}

您也可以返回一个 Promise

// import something here

export default ({ app, port, resolve, publicPath, folders, render, serve }) => {
  return new Promise((resolve, reject) => {
    // something to do with the server "app"
  })
}

您现在可以根据 SSR 中间件文件的预期用途向该文件添加内容。

最后一步是告诉 Quasar 使用您的新 SSR 中间件文件。为此,您需要在 /quasar.config 文件中添加该文件

/quasar.config 文件

ssr: {
  middlewares: [
    // references /src-ssr/middlewares/<name>.js
    '<name>'
  ]
}

在构建 SSR 应用程序时,您可能希望某些引导文件仅在生产环境中运行或仅在开发环境中运行,在这种情况下,您可以像下面这样操作

/quasar.config 文件

ssr: {
  middlewares: [
    ctx.prod ? '<name>' : '', // I run only on production!
    ctx.dev ? '<name>' : '' // I run only on development
  ]
}

如果您想指定来自 node_modules 的 SSR 中间件文件,可以通过在路径前添加 ~(波浪号)字符来实现。

/quasar.config 文件

ssr: {
  middlewares: [
    // boot file from an npm package
    '~my-npm-package/some/file'
  ]
}

警告

指定 SSR 中间件的顺序很重要,因为它决定了中间件应用于 Nodejs 服务器的方式。因此,它们会影响它如何响应客户端。

SSR 渲染中间件

重要!

在应用程序的所有可能的 SSR 中间件中,**此中间件是绝对必需的**,因为它处理使用 Vue 的实际 SSR 渲染。

在下面的示例中,我们强调此中间件需要位于列表的最后。这是因为它还使用页面的 HTML 响应客户端(我们将在下面的第二个代码示例中看到)。因此,任何后续中间件都不能设置标头。

/quasar.config 文件

ssr: {
  middlewares: [
    // ..... all other middlewares

    'render' // references /src-ssr/middlewares/render.js;
             // you can name the file however you want,
             // just make sure that it runs as last middleware
  ]
}

现在让我们看看它包含什么

src-ssr/middlewares/render.js

// This middleware should execute as last one
// since it captures everything and tries to
// render the page with Vue

export default ({ app, resolve, render, serve }) => {
  // we capture any other Express route and hand it
  // over to Vue and Vue Router to render our page
  app.get(resolve.urlPath('*'), (req, res) => {
    res.setHeader('Content-Type', 'text/html')

    render({ req, res })
      .then(html => {
        // now let's send the rendered html to the client
        res.send(html)
      })
      .catch(err => {
        // oops, we had an error while rendering the page

        // we were told to redirect to another URL
        if (err.url) {
          if (err.code) {
            res.redirect(err.code, err.url)
          }
          else {
            res.redirect(err.url)
          }
        }
        // hmm, Vue Router could not find the requested route
        else if (err.code === 404) {
          // Should reach here only if no "catch-all" route
          // is defined in /src/routes
          res.status(404).send('404 | Page Not Found')
        }
        // well, we treat any other code as error;
        // if we're in dev mode, then we can use Quasar CLI
        // to display a nice error page that contains the stack
        // and other useful information
        else if (process.env.DEV) {
          // serve.error is available on dev only
          serve.error({ err, req, res })
        }
        // we're in production, so we should have another method
        // to display something to the client when we encounter an error
        // (for security reasons, it's not ok to display the same wealth
        // of information as we do in development)
        else {
          // Render Error Page on production or
          // create a route (/src/routes) for an error page and redirect to it
          res.status(500).send('500 | Internal Server Error')
          // console.error(err.stack)
        }
      })
  })
}

请注意 render 参数(来自上面的代码示例),中间件的导出函数将使用它进行调用。SSR 渲染发生在其中。

热模块替换

在开发过程中,每当您更改 SSR 中间件中的任何内容时,Quasar App CLI 都会自动触发客户端资源的重新编译并将中间件更改应用于 Nodejs 服务器 (Expressjs)。

SSR 中间件示例

提示

您可以使用任何与 connect API 兼容的中间件。

日志记录器/拦截器

应用 SSR 中间件的顺序很重要。因此,明智的做法是将以下中间件设置为第一个(在 quasar.config 文件 > ssr > middlewares 中),以便它能够拦截所有客户端请求。

export default ({ app, resolve }) => {
  app.all(resolve.urlPath('*'), (req, _, next) => {
    console.log('someone requested:', req.url)
    next()
  })
}