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, port, resolve, publicPath, folders, render, serve }) => {
// something to do with the server "app"
}
SSR 中介層檔案也可以是 async
// import something here
export default async ({ app, port, resolve, publicPath, folders, render, serve }) => {
// something to do with the server "app"
await something()
}
您可以使用 ssrMiddleware
helper 包裝傳回的函式,以獲得更好的 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 中介層檔案的預設匯出函式所接收的 Object 參數。
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 應用程式實例。由於您將使用它來設定網路伺服器,因此它是任何中介層的「命脈」。
port
Node.js 網路伺服器的設定連接埠。
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」資料夾的路徑。opts
與express.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 })
content_paste
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
檔案中新增該檔案。
ssr: {
middlewares: [
// references /src-ssr/middlewares/<name>.js
'<name>'
]
}
建置 SSR 應用程式時,您可能希望某些啟動檔案僅在生產環境或僅在開發環境中執行,在這種情況下,您可以像下面這樣做。
ssr: {
middlewares: [
ctx.prod ? '<name>' : '', // I run only on production!
ctx.dev ? '<name>' : '' // I run only on development
]
}
如果您想從 node_modules 指定 SSR 中介層檔案,您可以透過在路徑前面加上 ~
(波浪符號)字元來做到。
ssr: {
middlewares: [
// boot file from an npm package
'~my-npm-package/some/file'
]
}
警告
您指定 SSR 中介層的順序很重要,因為它決定了中介層應用於 Nodejs 伺服器的方式。因此,它們會影響伺服器如何回應用戶端。
SSR 渲染中介層
重要!
在您應用程式中所有可能的 SSR 中介層中,**這個是絕對必要的**,因為它處理使用 Vue 的實際 SSR 渲染。
在下面的範例中,我們強調此中介層需要是列表中的最後一個。這是因為它也使用頁面的 HTML 回應用戶端(我們將在第二個程式碼範例中看到)。因此,任何後續的中介層都無法設定標頭。
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
]
}
現在讓我們看看它包含什麼。
// 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()
})
}