国产chinesehdxxxx野外,国产av无码专区亚洲av琪琪,播放男人添女人下边视频,成人国产精品一区二区免费看,chinese丰满人妻videos

Vite 服務端渲染(SSR)

2022-03-07 14:12 更新

源碼結構

一個典型的 SSR 應用應該有如下的源文件結構:

- index.html
- src/
  - main.js          # 導出環(huán)境無關的(通用的)應用代碼
  - entry-client.js  # 將應用掛載到一個 DOM 元素上
  - entry-server.js  # 使用某框架的 SSR API 渲染該應用

index.html 將需要引用 entry-client.js 并包含一個占位標記供給服務端渲染時注入:

<div id="app"><!--ssr-outlet--></div>
<script type="module" src="/src/entry-client.js"></script>

你可以使用任何你喜歡的占位標記來替代 <!--ssr-outlet-->,只要它能夠被正確替換。

情景邏輯

如果需要執(zhí)行 SSR 和客戶端間情景邏輯,可以使用:

if (import.meta.env.SSR) {
  // ... 僅在服務端執(zhí)行的邏輯
}

這是在構建過程中被靜態(tài)替換的,因此它將允許對未使用的條件分支進行搖樹優(yōu)化。

設置開發(fā)服務器

在構建 SSR 應用程序時,你可能希望完全控制主服務器,并將 Vite 與生產(chǎn)環(huán)境脫鉤。因此,建議以中間件模式使用 Vite。下面是一個關于 express 的例子:

server.js

const fs = require('fs')
const path = require('path')
const express = require('express')
const { createServer: createViteServer } = require('vite')

async function createServer() {
  const app = express()

  // 以中間件模式創(chuàng)建 Vite 應用,這將禁用 Vite 自身的 HTML 服務邏輯
  // 并讓上級服務器接管控制
  //
  // 如果你想使用 Vite 自己的 HTML 服務邏輯(將 Vite 作為
  // 一個開發(fā)中間件來使用),那么這里請用 'html'
  const vite = await createViteServer({
    server: { middlewareMode: 'ssr' }
  })
  // 使用 vite 的 Connect 實例作為中間件
  app.use(vite.middlewares)

  app.use('*', async (req, res) => {
    // 服務 index.html - 下面我們來處理這個問題
  })

  app.listen(3000)
}

createServer()

這里 vite 是 ViteDevServer 的一個實例。?vite.middlewares? 是一個 Connect 實例,它可以在任何一個兼容 connect 的 Node.js 框架中被用作一個中間件。

下一步是實現(xiàn) ?*? 處理程序供給服務端渲染的 HTML:

app.use('*', async (req, res) => {
  const url = req.originalUrl

  try {
    // 1. 讀取 index.html
    let template = fs.readFileSync(
      path.resolve(__dirname, 'index.html'),
      'utf-8'
    )

    // 2. 應用 Vite HTML 轉換。這將會注入 Vite HMR 客戶端,
    //    同時也會從 Vite 插件應用 HTML 轉換。
    //    例如:@vitejs/plugin-react-refresh 中的 global preambles
    template = await vite.transformIndexHtml(url, template)

    // 3. 加載服務器入口。vite.ssrLoadModule 將自動轉換
    //    你的 ESM 源碼使之可以在 Node.js 中運行!無需打包
    //    并提供類似 HMR 的根據(jù)情況隨時失效。
    const { render } = await vite.ssrLoadModule('/src/entry-server.js')

    // 4. 渲染應用的 HTML。這假設 entry-server.js 導出的 `render`
    //    函數(shù)調用了適當?shù)?SSR 框架 API。
    //    例如 ReactDOMServer.renderToString()
    const appHtml = await render(url)

    // 5. 注入渲染后的應用程序 HTML 到模板中。
    const html = template.replace(`<!--ssr-outlet-->`, appHtml)

    // 6. 返回渲染后的 HTML。
    res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
  } catch (e) {
    // 如果捕獲到了一個錯誤,讓 Vite 來修復該堆棧,這樣它就可以映射回
    // 你的實際源碼中。
    vite.ssrFixStacktrace(e)
    console.error(e)
    res.status(500).end(e.message)
  }
})

package.json 中的 dev 腳本也應該相應地改變,使用服務器腳本:

  "scripts": {
-   "dev": "vite"
+   "dev": "node server"
  }

生產(chǎn)環(huán)境構建

為了將 SSR 項目交付生產(chǎn),我們需要:

  1. 正常生成一個客戶端構建;
  2. 再生成一個 SSR 構建,使其通過 ?require()? 直接加載,這樣便無需再使用 Vite 的 ?ssrLoadModule?;

?package.json? 中的腳本應該看起來像這樣:

{
  "scripts": {
    "dev": "node server",
    "build:client": "vite build --outDir dist/client",
    "build:server": "vite build --outDir dist/server --ssr src/entry-server.js "
  }
}

注意使用 ?--ssr? 標志表明這將會是一個 SSR 構建。同時需要指定 SSR 的入口。

接著,在 ?server.js? 中,通過 ?process.env.NODE_ENV? 條件分支,需要添加一些用于生產(chǎn)環(huán)境的特定邏輯:

  • 使用 ?dist/client/index.html? 作為模板,而不是根目錄的 ?index.html?,因為前者包含了到客戶端構建的正確資源鏈接。
  • 使用 ?require('./dist/server/entry-server.js')? ,而不是 ?await vite.ssrLoadModule('/src/entry-server.js')?(前者是 SSR 構建后的最終結果)。
  • 將 ?vite ?開發(fā)服務器的創(chuàng)建和所有使用都移到 dev-only 條件分支后面,然后添加靜態(tài)文件服務中間件來服務 ?dist/client? 中的文件。

可以在此參考 Vue 和 React 的設置范例。

生成預加載指令

vite build 支持使用 --ssrManifest 標志,這將會在構建輸出目錄中生成一份 ssr-manifest.json

- "build:client": "vite build --outDir dist/client",
+ "build:client": "vite build --outDir dist/client --ssrManifest",

上面的腳本將會為客戶端構建生成 ?dist/client/ssr-manifest.json?(是的,該 SSR 清單是從客戶端構建生成而來,因為我們想要將模塊 ID 映射到客戶端文件上)。清單包含模塊 ID 到它們關聯(lián)的 chunk 和資源文件的映射。

為了利用該清單,框架需要提供一種方法來收集在服務器渲染調用期間使用到的組件模塊 ID。

?@vitejs/plugin-vue? 支持該功能,開箱即用,并會自動注冊使用的組件模塊 ID 到相關的 Vue SSR 上下文:

// src/entry-server.js
const ctx = {}
const html = await vueServerRenderer.renderToString(app, ctx)
// ctx.modules 現(xiàn)在是一個渲染期間使用的模塊 ID 的 Set

我們現(xiàn)在需要在 server.js 的生產(chǎn)環(huán)境分支下讀取該清單,并將其傳遞到 src/entry-server.js 導出的 render 函數(shù)中。這將為我們提供足夠的信息,來為異步路由相應的文件渲染預加載指令!

預渲染/SSG

如果預先知道某些路由所需的路由和數(shù)據(jù),我們可以使用與生產(chǎn)環(huán)境 SSR 相同的邏輯將這些路由預先渲染到靜態(tài) HTML 中。這也被視為一種靜態(tài)站點生成(SSG)的形式。

SSR外部化

許多依賴都同時提供 ESM 和 CommonJS 文件。當運行 SSR 時,提供 CommonJS 構建的依賴關系可以從 Vite 的 SSR 轉換/模塊系統(tǒng)進行 “外部化”,從而加速開發(fā)和構建。例如,并非去拉取 React 的預構建的 ESM 版本然后將其轉換回 Node.js 兼容版本,用 ?require('react')? 代替會更有效。它還大大提高了 SSR 包構建的速度。

Vite 基于以下策略執(zhí)行自動化的 SSR 外部化:

  • 如果一個依賴的解析 ESM 入口點和它的默認 Node 入口點不同,它的默認 Node 入口可能是一個可以外部化的 CommonJS 構建。例如,?vue? 將被自動外部化,因為它同時提供 ESM 和 CommonJS 構建。
  • 否則,Vite 將檢查包的入口點是否包含有效的 ESM 語法 - 如果不包含,這個包可能是 CommonJS,將被外部化。例如,?react-dom? 將被自動外部化,因為它只指定了唯一的一個 CommonJS 格式的入口。

如果這個策略導致了錯誤,你可以通過 ?ssr.external? 和 ?ssr.noExternal? 配置項手動調整。

在未來,這個策略將可能得到改進,將去探測該項目是否有啟用 ?type: "module"?,這樣 Vite 便可以在 SSR 期間通過動態(tài) ?import()? 導入兼容 Node 的 ESM 構建依賴來實現(xiàn)外部化依賴項。

使用別名

如果你為某個包配置了一個別名,為了能使 SSR 外部化依賴功能正常工作,你可能想要使用的別名應該指的是實際的 ?node_modules? 中的包。Yarn 和 pnpm 都支持通過 ?npm?: 前綴來設置別名。

SSR專有插件邏輯

一些框架,如 Vue 或 Svelte,會根據(jù)客戶端渲染和服務端渲染的區(qū)別,將組件編譯成不同的格式??梢韵蛞韵碌牟寮^子中,給 Vite 傳遞額外的 ?options對象,對象中包含 ?ssr?屬性來支持根據(jù)情景轉換:

  • ?resolveId?
  • ?load?
  • ?transform?

示例:

export function mySSRPlugin() {
  return {
    name: 'my-ssr',
    transform(code, id, options) {
      if (options?.ssr) {
        // 執(zhí)行 ssr 專有轉換...
      }
    }
  }
}

?options中的 ?load和 ?transform為可選項,rollup 目前并未使用該對象,但將來可能會用額外的元數(shù)據(jù)來擴展這些鉤子函數(shù)。

注意 Vite 2.7 之前的版本,會提示你 ?ssr參數(shù)的位置不應該是 ?options對象。目前所有主框架和插件都已對應更新,但你可能還會發(fā)現(xiàn)使用過時 API 的舊文章。

SSR Target

SSR 構建的默認目標為 node 環(huán)境,但你也可以讓服務運行在 Web Worker 上。每個平臺的打包條目解析是不同的。你可以將ssr.target 設置為 webworker,以將目標配置為 Web Worker。

SSR Bundle

在某些如 ?webworker運行時等特殊情況中,你可能想要將你的 SSR 打包成單個 JavaScript 文件。你可以通過設置 ?ssr.noExternal? 為 ?true來啟用這個行為。這將會做兩件事:

  • 將所有依賴視為 ?noExternal?(非外部化)
  • 若任何 Node.js 內置內容被引入,將拋出一個錯誤


以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號