Reading Time: 7 minutes

kích hoạt server-side rendering

Lưu ý: Hướng dẫn này chỉ là phần giới thiệu ngắn gọn về ReactDOM.hydrate()ReactDOMServer.renderToString(), không dùng cho môi trường production.

Thay vào đó, Next.js cung cấp một phương pháp hiện đại để tạo các ứng dụng tĩnh và ứng dụng được dựng từ phía máy chủ (server-rendered) bằng React.

Server-side rendering (SSR) là gì?

Server-side rendering là kỹ thuật phổ biến để render ứng dụng single-page (SPA) phía client trên server, sau đó gửi trang đã được render hoàn chỉnh tới client. Phương pháp này cho phép các thành phần động được phục vụ dưới dạng HTML markup tĩnh.

Kỹ thuật này hữu ích cho SEO khi các công cụ tìm kiếm không xử lý JavaScript đúng cách và trong trường hợp tải xuống gói JavaScript lớn bị ảnh hưởng bởi kết nối mạng chậm.

Trong hướng dẫn này, bạn sẽ khởi tạo một ứng dụng React bằng Create React App và sau đó chỉnh sửa dự án để kích hoạt server-side rendering.

Sau khi kết thúc bài hướng dẫn này, bạn sẽ có thể xây dựng được một dự án hoạt động với ứng dụng React phía client và ứng dụng Express phía server.

Yêu cầu tiên quyết

Để hoàn thành hướng dẫn, bạn cần cài đặt Node.js trên máy.

Hướng dẫn đã được thử nghiệm và hoạt động tốt với các phiên bản sau:

  • Node v16.13.1
  • npm v8.1.2
  • react v17.0.2
  • @babel/core v7.16.0
  • webpack v4.44.2
  • express v4.17.1
  • nodemon v2.0.15
  • npm-run-all v4.1.5

Các bước kích hoạt server-side rendering

Bước 1 – Tạo ứng dụng React và chỉnh sửa Component App

Đầu tiên, sử dụng npx để khởi động một ứng dụng React mới bằng phiên bản mới nhất của Create React App.

Hãy gọi ứng dụng là react-ssr-example:

npx create-react-app react-ssr-example

Sau đó, di chuyển vào thư mục mới bằng lệnh:

cd react-ssr-example

Cuối cùng, khởi động ứng dụng client-side mới để xác minh quá trình cài đặt:

npm start

Bạn sẽ thấy một ứng dụng React ví dụ được hiển thị trong cửa sổ trình duyệt của mình.

Bây giờ, trong thư mục src, hãy tạo một component <Home> mới:

nano src/Home.js

Tiếp theo, thêm đoạn mã sau vào tệp Home.js:

// src/Home.js function Home(props) { return <h1>Hello {props.name}!</h1>; } export default Home;

Thao tác này sẽ tạo ra một thẻ tiêu đề <h1> với thông điệp “Hello” hướng đến một tên cụ thể.

Tiếp theo, chúng ta hãy render component <Home> trong component <App>. Mở tệp App.js trong thư mục src:

nano src/App.js

Sau đó, thay thế các dòng code hiện có bằng các dòng code mới sau:

`// src/App.js import Home from ‘./Home’;

function App() { return <Home name=”Sammy”/>; } export default App;`

Lệnh này sẽ truyền một name cho component <Home> và hiển thị thông điệp là:

Output:

"Hello Sammy!"

Trong tệp index.js của ứng dụng, bạn sẽ sử dụng phương thức hydrate của ReactDOM thay vì render để thể hiện cho bộ dựng DOM thấy rằng bạn có ý định rehydrate ứng dụng sau khi được dựng từ phía máy chủ (server-side render).

Hãy mở tệp index.js trong thư mục src:

nano src/index.js

Sau đó, thay thế nội dung của tệp index.js bằng đoạn code sau:

`// src/index.js import React from ‘react’; import ReactDOM from ‘react-dom’; import App from ‘./App’;

ReactDOM.hydrate( <React.StrictMode> <App /> </React.StrictMode>, document.getElementById(‘root’) );`

Việc thiết lập phía client đã hoàn tất, bạn có thể chuyển sang thiết lập phía server.

Bước 2 – Tạo Express Server và Render Component App

Bây giờ khi bạn đã có ứng dụng, hãy thiết lập một máy chủ để gửi kèm theo một phiên bản đã được render (dựng sẵn). Bạn sẽ sử dụng Express cho máy chủ này.

Lưu ý: react-scripts của Create React App đã cài đặt một phiên bản của express như một yêu cầu cho webpack-dev-server. Để tránh xung đột cây phụ thuộc (dependency tree conflict), hướng dẫn này sẽ không bao gồm bước cài đặt này nữa.

Tiếp theo, hãy tạo một thư mục server mới trong thư mục gốc của dự án:

mkdir server

Sau đó, bên trong thư mục server, tạo một tệp index.js mới để chứa code server Express:

nano server/index.js

Thêm các import cần thiết và định nghĩa một số hằng số:

`// server/index.js import path from ‘path’; import fs from ‘fs’; import React from ‘react’; import ReactDOMServer from ‘react-dom/server’; import express from ‘express’; import App from ‘../src/App’; // Import component App từ phía client

const PORT = process.env.PORT || 3006; const app = express();`

Tiếp theo, thêm code server với một số xử lý lỗi:

// ...
app.get('/', (req, res) => {
const app = ReactDOMServer.renderToString(<App />);
const indexFile = path.resolve('./build/index.html');
fs.readFile(indexFile, 'utf8', (err, data) => {
if (err) {
console.error('Something went wrong:', err);
return res.status(500).send('Oops, better luck next time!');
}
return res.send(
  data.replace('<div id="root"></div>', `<div id="root">${app}</div>`)
);

});
});
app.use(express.static('./build'));
app.listen(PORT, () => {
console.log(Server is listening on port ${PORT});
});

Có thể import component <App> từ ứng dụng client trực tiếp vào máy chủ.

Ba điều quan trọng của quá trình trên là:

  • Express được sử dụng để phục vụ nội dung từ thư mục build như các tệp tĩnh.
  • ReactDOMServer.renderToString được dùng để render ứng dụng thành một chuỗi HTML tĩnh.
  • Sau khi đọc tệp index.html tĩnh đã được build từ phía client, nội dung của ứng dụng sẽ được đưa vào bên trong thẻ <div>id="root". Cuối cùng, toàn bộ nội dung này sẽ được gửi về làm phản hồi (response).

Kích hoạt server-side rendering

Bước 3 – Cấu hình webpack, Babel và npm Scripts

Để code server hoạt động, bạn sẽ cần đóng gói (bundle) và chuyển đổi mã (transpile) bằng cách sử dụng webpackBabel.

Lưu ý: Phần trước của hướng dẫn này đã chỉ cách cài đặt babel-core, babel-preset-env, và babel-preset-react-app. Các gói này đã được lưu trữ và các phiên bản mono repo đã được sử dụng thay thế.

react-scripts của Create React App xử lý việc cài đặt các gói sau: webpack, webpack-cli, webpack-node-externals, @babel/core, babel-loader, @babel/preset-env, @babel/preset-react. Để tránh dependency tree conflict, hướng dẫn này sẽ không đề cập đến bước cài đặt này nữa.

Tiếp theo, hãy tạo một tệp cấu hình Babel mới trong thư mục gốc của dự án:

nano .babelrc.json

Sau đó, thêm các preset envreact-app:

// .babelrc.json { "presets": [ "@babel/preset-env", "@babel/preset-react" ] }

Lưu ý: Phần trước của hướng dẫn này đã sử dụng tệp .babelrc (không có đuôi tệp .json). Đây là tệp cấu hình cho Babel 6, nhưng cấu hình này không phù hợp với Babel 7.

Bây giờ, hãy tạo một cấu hình webpack cho server sử dụng Babel Loader để chuyển đổi code. Bắt đầu bằng cách tạo tệp webpack.server.js trong thư mục gốc của dự án:

nano webpack.server.js

Sau đó, thêm các cấu hình sau vào tệp webpack.server.js:

`// webpack.server.js const path = require(‘path’); const nodeExternals = require(‘webpack-node-externals’);

module.exports = { entry: ‘./server/index.js’, target: ‘node’, externals: [nodeExternals()], output: { path: path.resolve(‘server-build’), filename: ‘index.js’ }, module: { rules: [ { test: /\.js$/, use: ‘babel-loader’ } ] } };`

Với cấu hình này, gói server đã được chuyển đổi sẽ được xuất ra thư mục server-build trong một tệp có tên index.js.

Lưu ý việc sử dụng target: 'node'externals: [nodeExternals()] từ webpack-node-externals sẽ thực hiện bỏ qua các tệp từ node_modules trong gói. Máy chủ có thể truy cập trực tiếp các tệp này.

Điều này hoàn tất việc cài đặt các phụ thuộc và cấu hình webpack và Babel.

Bây giờ, hãy xem lại tệp package.json và thêm các script trợ giúp của npm:

nano package.json

Thêm các script dev:build-server, dev:start, và dev vào tệp package.json để build và phục vụ ứng dụng SSR:

// package.json "scripts": { "dev:build-server": "NODE_ENV=development webpack --config webpack.server.js --mode=development -w", "dev:start": "nodemon ./server-build/index.js", "dev": "npm-run-all --parallel build dev:*", // ... (các script khác) },

Script dev:build-server đặt môi trường thành "development" và gọi webpack với tệp cấu hình bạn đã tạo trước đó. Script dev:start gọi nodemon để phục vụ đầu ra đã build.

Script dev gọi npm-run-all để chạy song song script build và tất cả các script bắt đầu bằng dev:* – bao gồm dev:build-serverdev:start.

Lưu ý: Bạn không cần sửa đổi các script hiện có "start", "build", "test", và "eject" trong tệp package.json.

nodemon được sử dụng để khởi động lại máy chủ khi có thay đổi. npm-run-all được dùng để chạy nhiều lệnh song song.

Hãy cài đặt các gói đó ngay bây giờ bằng cách nhập các lệnh sau vào cửa sổ terminal của bạn:

npm install nodemon@2.0.15 --save-dev npm install npm-run-all@4.1.5 --save-dev

Với những thiết lập này, bạn có thể chạy lệnh sau để build ứng dụng client-side, đóng gói và chuyển đổi code server, và khởi động server trên cổng :3006:

npm run dev

Cấu hình webpack của server bây giờ sẽ theo dõi các thay đổi và server sẽ tự khởi động lại khi có thay đổi. Tuy nhiên, đối với ứng dụng client, bạn sẽ cần build lại thủ công mỗi khi có thay đổi.

Bây giờ, hãy mở http://localhost:3006/ trong trình duyệt web của bạn và quan sát ứng dụng đã được render từ phía máy chủ.

Trước đây, khi xem code sẽ hiển thị:

Kết quả:

<div id="root"></div>

Nhưng bây giờ, với những thay đổi bạn đã thực hiện, code sẽ hiển thị như sau:

Kết quả:

<div id="root"><h1 data-reactroot="">Hello Sammy!</h1></div>

Việc render từ phía server đã thành công chuyển đổi component <App> thành HTML.

Kết luận

Vậy là chúng ta đã hoàn thành việc kích hoạt server-side rendering cho ứng dụng React.

Với bài viết này, chúng ta chỉ mới thảo luận những khái niệm rất cơ bản. Đi sâu hơn vào chủ đề này thì mọi thứ trở nên phức tạp hơn một chút khi định tuyến (routing), tìm nạp dữ liệu (data fetching) hoặc Redux cũng cần được tích hợp vào một ứng dụng SSR.

Một ưu điểm lớn của việc dùng SSR là ứng dụng của bạn có thể được các công cụ tìm kiếm thu thập nội dung hiệu quả hơn, kể cả với những “bot” không chạy được JavaScript. Điều này rất hữu ích nếu bạn muốn tối ưu hóa công cụ tìm kiếm (SEO) và giúp bạn cung cấp siêu dữ liệu (metadata) đầy đủ cho các kênh mạng xã hội.

SSR cũng thường cải thiện hiệu suất vì ngay trong yêu cầu đầu tiên, server sẽ gửi về một ứng dụng đã được tải đầy đủ. Tuy nhiên, với các ứng dụng phức tạp, bạn có thể thấy hiệu quả không như mong đợi. Lý do là việc thiết lập SSR có thể khá phức tạp và nó tạo ra tải trọng lớn hơn cho server. Quyết định có nên dùng SSR cho ứng dụng React của bạn hay không phụ thuộc vào nhu cầu cụ thể và việc cân nhắc những ưu nhược điểm nào phù hợp nhất với dự án của bạn.

0 Bình luận

Đăng nhập để thảo luận

Chuyên mục Hướng dẫn

Tổng hợp các bài viết hướng dẫn, nghiên cứu và phân tích chi tiết về kỹ thuật, các xu hướng công nghệ mới nhất dành cho lập trình viên.

Đăng ký nhận bản tin của chúng tôi

Hãy trở thành người nhận được các nội dung hữu ích của CyStack sớm nhất

Xem chính sách của chúng tôi Chính sách bảo mật.

Đăng ký nhận Newsletter

Nhận các nội dung hữu ích mới nhất