Dark mode
Source Structure
โครงสร้างไฟล์พื้นฐานที่จำเป็นสำหรับการทำ Server-Side Rendering (SSR) ใน Vite ซึ่งประกอบด้วยไฟล์หลักๆ สำหรับทั้งฝั่งเซิร์ฟเวอร์และไคลเอนต์
โครงสร้างไฟล์สำหรับ SSR ใน Vite จะมีลักษณะดังนี้:
bash
- index.html # ไฟล์เทมเพลตหลัก
- server.js # เซิร์ฟเวอร์หลัก
- src/
├── main.js # โค้ดแอปที่ใช้ร่วมกัน (universal)
├── entry-client.js # จัดการ hydration บนไคลเอนต์
└── entry-server.js # จัดการ rendering บนเซิร์ฟเวอร์
ตัวอย่าง index.html
html
<!-- ไฟล์เทมเพลตหลัก -->
<div id="app"><!--ssr-outlet--></div>
<script type="module" src="/src/entry-client.js"></script>
<!--ssr-outlet-->
เป็น placeholder สำหรับ HTML ที่ render จากเซิร์ฟเวอร์- สามารถใช้ placeholder อื่นแทนได้ตามต้องการ
ตัวอย่าง entry-server.js
ts
// ส่งออกฟังก์ชัน render สำหรับเซิร์ฟเวอร์
export async function render(url) {
// โค้ดเฉพาะ SSR จะอยู่ที่นี่
const html = await renderToString(app);
return html;
}
ตัวอย่างการเพิ่มโค้ด SSR
ts
// ตัวอย่างการเพิ่มโค้ด SSR ใน entry-server.js
import { createSSRApp } from "vue";
export async function render(url) {
const app = createSSRApp(App);
const html = await renderToString(app);
return html;
}
Conditional Logic
เทคนิคการเขียนโค้ดที่สามารถทำงานต่างกันระหว่างเซิร์ฟเวอร์และไคลเอนต์ โดยใช้ฟีเจอร์ของ Vite ในการตรวจสอบสภาพแวดล้อม
เมื่อต้องการเขียนโค้ดที่ทำงานต่างกันระหว่างเซิร์ฟเวอร์และไคลเอนต์:
ts
// ใช้ import.meta.env.SSR เพื่อตรวจสอบว่าโค้ดทำงานบนเซิร์ฟเวอร์หรือไม่
// ตัวแปรนี้จะถูกแทนที่ตอน build ทำให้สามารถตัดโค้ดที่ไม่ใช้ออกได้ (tree-shaking)
if (import.meta.env.SSR) {
// โค้ดนี้จะทำงานเฉพาะบนเซิร์ฟเวอร์
console.log("Running on server");
} else {
// โค้ดนี้จะทำงานเฉพาะบนไคลเอนต์
console.log("Running on client");
}
Setting Up the Dev Server
ขั้นตอนการตั้งค่าเซิร์ฟเวอร์พัฒนาแบบ SSR ด้วย Express และ Vite middleware สำหรับการพัฒนาแอปพลิเคชัน
ts
// การตั้งค่า Express server พร้อม Vite middleware สำหรับโหมดพัฒนา
// ใช้สำหรับพัฒนาแอปพลิเคชันแบบ SSR
import express from "express";
import { createServer as createViteServer } from "vite";
async function createServer() {
const app = express();
// สร้าง Vite server ในโหมด middleware
// - middlewareMode: true ทำให้ Vite ทำงานเป็น middleware แทนที่จะเป็นเซิร์ฟเวอร์เต็มรูปแบบ
// - appType: 'custom' ปิดการใช้งาน HTML serving ของ Vite
const vite = await createViteServer({
server: { middlewareMode: true },
appType: "custom",
});
// ใช้ Vite middleware เพื่อให้สามารถใช้งาน HMR และคุณสมบัติอื่นๆ ของ Vite ได้
app.use(vite.middlewares);
// เริ่มต้นเซิร์ฟเวอร์ที่พอร์ต 3000
app.listen(3000, () => {
console.log("Server running at http://localhost:3000");
});
}
createServer();
Building for Production
กระบวนการ build แอปพลิเคชัน SSR สำหรับ production แยกเป็นส่วนของไคลเอนต์และเซิร์ฟเวอร์
สคริปต์ build ใน package.json
json
{
"scripts": {
"build:client": "vite build --outDir dist/client",
"build:server": "vite build --outDir dist/server --ssr src/entry-server.js"
}
}
build:client
สร้างไฟล์สำหรับไคลเอนต์build:server
สร้างไฟล์สำหรับเซิร์ฟเวอร์ (ใช้ flag --ssr)
การปรับเปลี่ยน server.js สำหรับ production
ts
// ในโหมด production
if (process.env.NODE_ENV === "production") {
// ใช้ไฟล์จาก dist/client เป็นเทมเพลต
const template = fs.readFileSync("dist/client/index.html", "utf-8");
// โหลด entry-server.js จากผลลัพธ์การ build
const { render } = await import("./dist/server/entry-server.js");
// เสิร์ฟไฟล์ static จาก dist/client
app.use(express.static("dist/client"));
}
Generating Preload Directives
วิธีการสร้างและใช้งาน SSR Manifest เพื่อเพิ่มประสิทธิภาพการโหลดทรัพยากร
การสร้าง SSR Manifest
ts
// การตั้งค่า script ใน package.json สำหรับสร้าง manifest file
{
"scripts": {
// คำสั่ง build สำหรับ client พร้อมสร้าง manifest
"build:client": "vite build --outDir dist/client --ssrManifest"
}
}
// เพิ่ม flag `--ssrManifest` เพื่อสร้างไฟล์ manifest
// ไฟล์จะถูกสร้างที่ `dist/client/.vite/ssr-manifest.json`
// ใช้ map module IDs ไปยัง client files
การใช้งานกับ Vue
ts
// การใช้งาน Vue SSR Context เพื่อเก็บ module IDs
// สร้าง context object สำหรับเก็บข้อมูลระหว่าง render
const ctx = {};
// Render หน้าเป็น HTML และเก็บ module IDs ที่ใช้ใน ctx
const html = await vueServerRenderer.renderToString(app, ctx);
// ctx.modules จะมี Set ของ module IDs ที่ใช้
// `@vitejs/plugin-vue` รองรับการเก็บ module IDs อัตโนมัติ
// ใช้ข้อมูลนี้เพื่อสร้าง preload directives
การอ่าน manifest ใน production
ts
// ในโหมด production
const manifest = fs.readFileSync(
"dist/client/.vite/ssr-manifest.json",
"utf-8",
);
const html = await render(url, manifest);
- ส่ง manifest ไปยังฟังก์ชัน render
- ใช้สร้าง preload directives สำหรับ async routes
Pre-Rendering / SSG
Static Site Generation
ts
// ตัวอย่างสคริปต์ pre-render
const routes = ["/", "/about", "/contact"];
for (const url of routes) {
const html = await render(url);
fs.writeFileSync(`dist/static${url}.html`, html);
}
- ใช้ logic เดียวกับ production SSR
- เหมาะสำหรับ routes ที่รู้ข้อมูลล่วงหน้า
- สามารถใช้เป็นรูปแบบ Static Site Generation (SSG)
SSR Externals
การตั้งค่า dependencies สำหรับ SSR ในไฟล์ vite.config.js เพื่อควบคุมการทำงานของ packages ต่างๆ
การตั้งค่าใน vite.config.js
ts
export default {
ssr: {
noExternal: ["package-to-transform"],
external: ["linked-package"],
},
};
- Dependency ส่วนใหญ่จะถูก externalize โดยอัตโนมัติ
- ใช้
noExternal
สำหรับ dependency ที่ต้องผ่านการ transform - ใช้
external
สำหรับ dependency ที่ต้องการให้ทำงานเหมือนไม่ถูก link
การทำงานกับ Aliases
bash
# ใน Yarn/pnpm ใช้ npm: prefix
"dependencies": {
"my-alias": "npm:actual-package@^1.0.0"
}
SSR-specific Plugin Logic
ตัวอย่าง Plugin สำหรับ SSR
ts
export function mySSRPlugin() {
return {
name: "my-ssr",
transform(code, id, options) {
if (options?.ssr) {
// ทำการ transform เฉพาะ SSR
return transformSSRCode(code);
}
return code;
},
};
}
- Vite ส่งค่า
ssr
ใน options object ให้ plugin - ทำงานกับ hooks:
resolveId
,load
,transform
- ใช้สำหรับแปลงโค้ดต่างกันระหว่าง SSR และ Client
SSR Target
การตั้งค่า target สำหรับ SSR build
ts
export default {
ssr: {
target: "node", // หรือ 'webworker'
},
};
node
(ค่าเริ่มต้น): สำหรับการรันบน Node.jswebworker
: สำหรับการรันบน Web Worker- แต่ละ platform มีวิธี resolve packages ที่ต่างกัน
SSR Bundle
SSR Resolve Conditions
การกำหนดเงื่อนไขการ resolve packages
ts
export default {
ssr: {
resolve: {
conditions: ["my-ssr-condition"],
externalConditions: ["node"],
},
},
};
conditions
: เงื่อนไขสำหรับ SSR buildexternalConditions
: เงื่อนไขสำหรับ externalized dependencies- ค่าเริ่มต้นใช้จาก
resolve.conditions
Vite CLI
การใช้ CLI กับ SSR
ts
export default {
server: {
configureServer(server) {
// เพิ่ม SSR middleware
server.middlewares.use((req, res, next) => {
// SSR logic
});
},
},
};
- ใช้
configureServer
สำหรับ development server - ใช้
configurePreviewServer
สำหรับ preview server - ควรใช้ post hook เพื่อให้ middleware ทำงานหลัง Vite middlewares