Server Side Rendering trong ReactJS 🎉

nodejs

nodejs

reactjs

reactjs

Sáng Trần

Sáng Trần

/
2023-06-30
some ting wong

Hiện nay SSR đối với các Frontend Framework rất nổi tiếng điển hình là NextJS, Remix, Gastby, … của nhà React; NuxtJs của nhà Vue, SvelteKit của nhà Svelte, … Qua bài viết này chúng ta sẽ tìm hiểu cơ chế và làm thế nào để thực hiện SSR biểu diễn bằng ReactJS

Mục lục

CSR là gì ?

Client Side Rendering được diễn ra khi chúng ta nhận được từ request là 1 file HTML trống (như file index.html trong React App) và sau đó quá trình thêm JS vào để render ra toàn bộ content được diễn ra ở phía Browser(Client)

Nhờ vậy điểm mạnh của CSR sẽ giúp:

  • Performance: cải thiện hiệu suất hiệu quả sau khi được load lên(After initial load), đó là vì Browser load Javascript để chạy đúng 1 lần duy nhất nên những trang khác ở web đó đều rất nhanh
  • Flexibility: các Web Application rất linh hoạt do toàn bộ ứng dụng để không phụ thuộc vào Server mà chỉ hiển thị ở Browser
  • Scalability: khả năng mở rộng và phát triển tốt hơn do không cần phải handle việc cả triệu request

Nhưng thực tế 1 ứng dụng xịn xò người ta sẽ kết hợp rất nhiều phương pháp như SSR, CSR, SSG, ISR, … chứ không đơn thuần là CSR

Ở đây ta chỉ so sánh CSRSSR còn SSGISR tương lai mình sẽ làm sau

SSR là gì ?

Server Side Rendering được diễn ra khi ta nhận về file HTML được render đầy đủ từ bên Server và sau đó dùng JS để hydrate nên ta sẽ có được giao diện animation, chức năng.

Khác biệt lớn nhất với CSR thay vì Server trả ra file HTML trống và dùng JS render ra ở Browser thì SSR Server trả ra file HTML đã được render đầy đủ element sau đó mới dùng JS hydrate(như kiểu nhúng) ở Browser. Chính vì thế SSR rất được ưa chuộng cho việc SEO

Nhờ vậy điểm mạnh của SSR sẽ giúp:

  • SEO: do content HTML được trả về đầy đủ sẽ giúp search engines có thể bắt được. Bởi vì content được Server render trước khi gửi cho Browser
  • Initial load speed: Initial load của SSR nhanh hơn CSR. Vì Browser không cần phải chờ JS load trước khi trang được render
  • Better performance when slow internet connections: Y như trên, do Browser không cần phải tải JS rồi chờ load trước khi được render

Được trả về đầy đủ content HTML content mà không cần JS ở Browser render ra nên ta nếu ta tắt JS ở Browser SSR sẽ hiển thị đc HTML còn CSR thì bắt buộc yêu cầu mở JS

Cách hoạt động giữa CSR và SSR trong React

Ở trên mình đã nêu ưu điểm riêng của mỗi phương pháp rồi, section này mình sẽ làm rõ cách hoạt động khác biệt của 2 cách

Đầu tiên là CSR:

  1. Request tới trang sangtran.dev
  2. Server(nodeJS) trả về HTML/CSS/JS file cho Browser
  3. Browser chờ JS và thực thi JS bằng JS engine
  4. Content HTML được render trên Browser cho User tương tác

Tiếp theo là SSR:

  1. Request tới trang sangtran.dev
  2. Server(nodeJS) render trang HTML sử dụng ReactJS
  3. Server gửi về file HTML đã được render về cho Browser
  4. Browser render lên cho User tương tác

Các bạn lưu ý, điều này không có nghĩa là toàn bộ ReactJS được thực thi ở SSR đâu nha. SSR có nhiệm vụ là khởi tạo lần đầu ra toàn bộ content được render trả ra cho Browser, các việc như useState, useEffect, … đều được diễn ra toàn bộ ở Browser hết. Rất nhiều bạn hiểu lầm về vấn đề này.

Hiện thực hoá SSR trong React bằng Express

Để hiểu rõ hơn về SSR mình cùng code thôi. Ví dụ lần này rất cơ bản nên sẽ dễ hiểu

Ở đây mình sẽ xài template của Vite và Server sẽ là ExpressJS

Đầu tiên tạo project ReactJS bằng Vite như bình thường

yarn create vite

Vào file index.js đổi từ render() sang hydrate()

ReactDOM.createRoot(document.getElementById('root')).hydrate(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
)

Sau khi tạo project thành công, tạo file server.js

import express from "express";
import fs from "fs";
import path from "path";
import React from "react";
import ReactDOMServer from "react-dom/server";
import App from "./src/App.jsx";

const PORT = 3030;
const app = express();

app.get("/", (req, res, next) => {
  fs.readFile(path.resolve("./dist/index.html"), "utf-8", (err, data) => {
    if (err) {
      res.status(500).send(err.message);
    }
    const reactDOM = ReactDOMServer.renderToString(<App />);
    return res.send(
      data.replace('<div id="root"></div>', `<div id="root">${reactDOM}</div>`)
    );
  });
});
app.use(express.static(path.resolve(__dirname, ".", "dist")));

app.listen(PORT, () => {
  console.log(`running at port: ${PORT}`);
});

Ở phía Server mình cần React để render html rồi sau đó gửi về cho Browser

Nhưng vấn đề là trong server.js vẫn chưa chạy được vì nó không hiểu được JSX, vì vậy ta cần cài thêm plugin

yarn add babel/preset-env @babel/preset-react @babel/register ignore-styles

Sau khi cài xong ta tạo thêm file index.js để config

require("ignore-styles");

require("@babel/register")({
  ignore: [/(node_modules)/],
  presets: ["@babel/preset-env", "@babel/preset-react"],
});

require("./server.js");

Nhiêu đó là đủ, cùng chạy thôi, chứ là chạy file index.js nha, không phải server.js

node index.js

Và đây là kết quả, ta có thể tương tác thêm State như bình thường

final result

Đặc biệt khi bạn tắt JS ở Browser, nó vẫn show ra được HTML đã được render từ Server trước đó trả về mà không cần JS, nhưng ta sẽ không tương tác được chẳng hạn như setCount. Vì các hành động đó cần có JS và được diễn ra ở Browser

final result

Qua bài viết này chúng ta có thể hiểu được phần nào về SSR trong Frontend nói chung và React nói riêng bằng ví dụ. Tuy nhiên đây chỉ là ví dụ cơ bản để hiện thực cách SSR hoạt động thôi, thực tế bao nhiêu đó vẫn chưa đủ ví dụ đoạn code không xài được multiple route

Thực tế ta nên sử dụng các Framework được hỗ trợ rất mạnh mẽ và có cộng đồng ví dụ như Nextjs. Nếu có thời gian bạn nên dành vài phút sẽ hiểu được SSR của Nextjs

Hi vọng bài chia sẻ này hữu ích với mọi người!

Mở rộng thêm bạn có thể tham khảo:

Cheers 🍺

Đã có 3 lượt bình luận

user-avatar
user-avatar
Kongou

-

a year ago

xịn z

user-avatar
lamtailoi2

-

a year ago

hay quá

user-avatar
Trần Quang Sáng

-

a year ago

Cảm ơn mn đã đọc qua 🥳🥳