Trong thời đại phát triển hướng máy chủ, việc gửi mã HTML đến client từ các template trên server là rất cần thiết. Bài viết này sẽ hướng dẫn các cách chính để thực hiện điều này bằng JavaScript, đồng thời áp dụng được cho các ngôn ngữ lập trình khác.
Phần 1: Xây dựng Backend và API Route
Với backend, chúng ta cần tạo một API route để lấy HTML từ server gửi về client. Đầu tiên, ta tạo mã HTML của form liên hệ đơn giản, có thể chỉnh sửa trên cả server và client, và dùng cho nhiều website liên kết:
form.html
<h1>Contact Us</h1>
<form action="/submit" method="POST"> <label for="name">Name:</label> <input type="text" id="name" name="name" required><br><br> <label for="email">Email:</label> <input type="email" id="email" name="email" required><br><br> <label for="message">Message:</label><br> <textarea id="message" name="message" rows="4" cols="50" required></textarea><br><br> <button type="submit">Submit</button>
</form>
Tiếp theo, ta tạo API route để nhận HTML từ server. Sử dụng Node.js với framework Express.js, ta định nghĩa controller xử lý HTML:
const express = require("express");
const expressRouter = express.Router();
const path = require("path"); const formController = (req, res) => { res.sendFile(path.join(__dirname, "../form.html"));
}; expressRouter.use("/getForm", formController);
Sau đó, kết nối controller tới /api trong file app.js để phân biệt với các route thông thường:
const express = require("express");
const path = require("path");
const bodyParser = require("body-parser");
const cors = require("cors");
const PORT = 8000;
const app = express();
const routes = require("./routes/formController"); app.use(bodyParser.urlencoded({ extended: false }));
app.use(cors({ origin: true, credentials: true })); app.set(express.static(path.join(__dirname, "src"))); app.use("/api", routes); app.listen(PORT);
Như vậy, ta đã có route /api/getForm để gửi request GET và nhận lại HTML.
Phần 2: Lấy HTML từ phía Client
Phía client, có nhiều cách để lấy HTML từ server, đều dựa trên việc gửi request. Trong JavaScript, ta sử dụng fetch (khuyến nghị không dùng XMLHTTPRequest vì đã cũ). Dưới đây là hai phương pháp chính:
1. Sử dụng thư viện HMPL
HMPL là ngôn ngữ template để hiển thị UI từ server sang client, dựa trên các request tùy chỉnh được gửi đến server và xử lý thành HTML. Cài đặt hmpl-js và import hàm compile:
npm i hmpl-js
import { compile } from "hmpl-js"; const templateFn = compile( `{{ src: "/api/getForm" }}`
); const form = templateFn();
Kết quả trả về:
form = { response: <template><h1>Contact Us</h1><form action="/submit" method="...</template>, status: 200
}
Ta nhận được template chứa form. Có thể thêm div vào chuỗi để nhận trực tiếp element div:
import { compile } from "hmpl-js"; const templateFn = compile( `<div>{{ src: "/api/getForm" }}</div>`
); const form = templateFn(); /* form = { response: <div><h1>Contact Us</h1><form...</div>, status: 200 }
*/
Ưu điểm:
a. Khả năng tái sử dụng: Bạn có thể sử dụng lại một mẫu được tạo một lần nhiều lần tùy ý, giống như việc tạo các phiên bản của class.
b. Cú pháp đơn giản và rõ ràng: HTML giới thiệu một đối tượng yêu cầu, có cú pháp hoàn toàn giống với vanilla, do đó, khi làm việc trong js, bạn có thể dễ dàng sao chép mã và không phải lo lắng, vì mô-đun này hoạt động trên JSON5.
c. Có thể tùy chỉnh: Ngôn ngữ mẫu cung cấp nhiều tùy chỉnh yêu cầu. Không giống như các dự án tương tự như HTMX, bạn có thể kiểm soát gần như hoàn toàn luồng.
d. Nhẹ: Nó không hề nặng, khoảng hơn 15 kilobyte, hầu như không ảnh hưởng gì đến dự án.
e. Khả năng tương thích với trình duyệt: Các API JavaScript hiện đại như fetch()được hỗ trợ rộng rãi trong các trình duyệt, đảm bảo khả năng tương thích mà không cần thêm polyfill cho hầu hết các trường hợp sử dụng.
Nhược điểm:
a. Phụ thuộc: Bằng cách kết nối một mô-đun, bạn kết nối mã bổ sung vào dự án, mã này có trọng lượng một số byte nhất định.
b. Giới hạn API hiện đại trong các trình duyệt cũ: Mặc dù được hỗ trợ rộng rãi, các tính năng như fetch()có thể yêu cầu polyfill cho các trình duyệt cũ hơn, không giống như một số thư viện xử lý khả năng tương thích ngược.
2. Sử dụng JavaScript thuần
Tạo file script và gửi request đến server:
main.js
fetch('/api/getForm') // URL to the HTML snippet
.then(response => { if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } return response.text();
})
.then(html => { console.log(html)
})
.catch(error => { console.error('Error fetching HTML:', error);
});
Đoạn mã này nhận response từ server. Nếu thành công, hàm text sẽ được gọi để nhận dữ liệu. Lưu ý xử lý mã HTTP 100 (chuyển hướng) và các lỗi khác. Tham khảo thêm về mã HTTP.
Ưu điểm:
a. Nhẹ và không phụ thuộc: Sử dụng JavaScript thuần túy giúp loại bỏ nhu cầu về các thư viện hoặc khuôn khổ bổ sung, giúp giảm kích thước và sự phụ thuộc chung của dự án.
b. Kiểm soát hoàn toàn: JavaScript thuần túy cung cấp cho bạn khả năng kiểm soát hoàn toàn các chi tiết triển khai, cho phép bạn xử lý các yêu cầu mạng, phản hồi và thao tác DOM chính xác theo nhu cầu.
c. Khả năng tương thích với trình duyệt: Các API JavaScript hiện đại như fetch() được hỗ trợ rộng rãi trong các trình duyệt, đảm bảo khả năng tương thích mà không cần thêm polyfill cho hầu hết các trường hợp sử dụng.
d. Cơ hội học tập: Làm việc với JavaScript thô giúp các nhà phát triển hiểu được các khái niệm cơ bản, chẳng hạn như yêu cầu HTTP, phản hồi và DOM, từ đó cải thiện kỹ năng lập trình của họ.
Nhược điểm:
a. Mã dài dòng: JavaScript thông thường thường yêu cầu nhiều mã mẫu hơn so với các thư viện như Axios hoặc các khuôn khổ như React, có thể xử lý các yêu cầu và cập nhật ngắn gọn hơn.
b. Xử lý lỗi: Việc quản lý lỗi (ví dụ: sự cố mạng, phản hồi không hợp lệ) có thể trở nên phức tạp và lặp đi lặp lại nếu không có sự trừu tượng hóa do các thư viện cấp cao hơn cung cấp.
c. Thiếu tính trừu tượng: Các tác vụ như xử lý thời gian chờ, thử lại hoặc yêu cầu đồng thời phải được thực hiện thủ công, điều này có thể rất nhàm chán và dễ xảy ra lỗi.
d. Khả năng bảo trì: Mã được viết hoàn toàn bằng JavaScript có thể khó bảo trì hơn trong các dự án lớn do tính rườm rà và thiếu các mẫu chuẩn hóa.
e. Giới hạn API hiện đại trong các trình duyệt cũ: Mặc dù được hỗ trợ rộng rãi, các tính năng như fetch()có thể yêu cầu polyfill cho các trình duyệt cũ hơn, không giống như một số thư viện xử lý khả năng tương thích ngược.
Phần kết luận
Tùy thuộc vào tình huống, bạn có thể sử dụng cách tiếp cận đầu tiên hoặc cách thứ hai. Trong cách thứ hai, bạn có toàn quyền kiểm soát quy trình, nhưng vẫn không phù hợp khi bạn cần làm việc với nhiều thành phần, vì bạn sẽ phải tạo logic của riêng mình và điều này tốn thời gian.