為何捐款
API 瀏覽器
升級指南
NEW!
quasar.config 檔案
轉換為搭配 Webpack 的 CLI
瀏覽器相容性
支援 TypeScript
目錄結構
指令列表
CSS 預處理器
路由
延遲載入 - 程式碼分割
處理資源
啟動檔案
預取功能
API 代理
處理 Webpack
處理 process.env
使用 Pinia 的狀態管理
使用 Vuex 的狀態管理
Linter
測試與稽核
開發行動應用程式
Ajax 請求
開放開發伺服器給公眾
搭配 Webpack 的 Quasar CLI - @quasar/app-webpack
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) 應用程式時,將會呼叫匯出的函式,並額外傳遞一個 Object 作為參數(這將在下一節中詳細說明)。

// import something here

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

SSR 中介層檔案也可以是異步的

// import something here

export default async ({ app, 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, resolve, publicPath, folders, render, serve }) => {
  // something to do
  await something()
})

請注意,我們正在使用 ES6 解構賦值。僅賦值您實際需要/使用的內容。

中介層物件參數

這裡指的是 SSR 中介層檔案的預設匯出函式接收作為參數的 Object。

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

物件詳情

{
  app, // Expressjs app instance
  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

這是 Expressjs 應用程式實例。由於您將使用它來配置網頁伺服器,因此它是任何中介層的「命脈」。

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, resolveUrlPath, publicPath, folders, render }) => {
  // something to do with the server "app"
}

您也可以傳回 Promise

// import something here

export default ({ app, 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')
        }
      })
  })
}

請注意中介層的匯出函式被呼叫時使用的 render 參數(來自上面的程式碼範例)。SSR 渲染就在這裡發生。

熱模組重載

在開發期間,每當您變更 SSR 中介層中的任何內容時,Quasar App CLI 都會自動觸發用戶端資源的重新編譯,並將中介層變更套用至 Nodejs 伺服器 (Expressjs)。

SSR 中介層的範例

提示

您可以使用任何相容於 Expressjs 的中介層。

壓縮

這個中介層僅在生產環境中使用才有意義。

import compression from 'compression'

export default ({ app }) => {
  app.use(
    compression({ threshold: 0 })
  )
}

記錄器 / 攔截器

SSR 中介層的應用順序很重要。因此,將以下中介層設定為第一個(在 quasar.config 檔案 > ssr > middlewares 中)可能是明智之舉,以便它能夠攔截所有客戶端請求。

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