0
0
Lập trình
Harry Tran
Harry Tran106580903228332612117

Trở Về Đơn Giản: Chuyển Đổi Web Hiện Đại

Đăng vào 2 ngày trước

• 7 phút đọc

Giới Thiệu

Chúng ta hãy cùng điểm lại sự tiến hóa của web cho đến nay:

  • Phần 1: Backend là vua - nó xử lý việc tạo trang, quản lý định tuyến và logic.
  • Phần 2: AJAX và jQuery xuất hiện, cho phép chúng ta cập nhật các phần của trang mà không cần tải lại.
  • Phần 3: SPAs chiếm ưu thế - chuyển đổi định tuyến, xác thực và việc hiển thị sang phía frontend. Hệ sinh thái JavaScript bùng nổ với độ phức tạp.

Bây giờ trong Phần 4, chúng ta đang chứng kiến một sự chuyển mình mới: Trở về với tư duy server-first, nâng cao dần và kiến trúc web nhẹ nhàng hơn - không phải là quay ngược lại, mà là kết hợp những điều tốt nhất của cả hai thế giới.

Chúng ta đang xây dựng các ứng dụng nhanh chóng, tương tác và thân thiện với SEO - mà không làm quá tải trình duyệt.


Những Vấn Đề Mà SPAs Gây Ra

SPAs đã giải quyết những điểm đau thực sự: định tuyến mượt mà, tương tác phong phú, hành vi giống như ứng dụng. Nhưng chúng cũng mang lại những chi phí:

  • Mỗi <form> đều phải được can thiệp bởi JavaScript.
  • Việc lấy dữ liệu bị rối rắm trong useEffect, useQuery, và các custom hooks.
  • Định tuyến trở nên hoàn toàn phụ thuộc vào JavaScript.
  • Xác thực bị lặp lại cả ở frontend và backend.
  • Các trang vẫn trắng cho đến khi quá trình hydration hoàn tất.
  • Các gói JS lớn làm ảnh hưởng đến hiệu suất và thời gian tải.
  • SEO bị ảnh hưởng nghiêm trọng mà không có giải pháp khả thi.
  • Quá trình xây dựng trở nên mong manh và phức tạp hơn.

Khi cố gắng xây dựng trải nghiệm người dùng tốt hơn, chúng ta đã làm cho phát triển trở nên khó khăn hơn và các website trở nên chậm chạp hơn.


Server-First, Client-Smart

Thay vì đổ tất cả lên client, chúng ta đang quay lại - giao cho server những gì mà nó luôn làm tốt nhất:

  • Định tuyến
  • Xác thực
  • Xử lý dữ liệu
  • Hiển thị giao diện người dùng

Nhưng với một cách tiếp cận mới: Chúng ta vẫn sử dụng JavaScript - một cách chọn lọc, chính xác và thông minh.

Cách tiếp cận này thường được gọi là nâng cao dần hoặc HTML-over-the-wire. Nó mang lại:

  • Tải trang ban đầu nhanh hơn
  • Các gói JS nhỏ hơn
  • SEO được cải thiện
  • Kiến trúc đơn giản hơn

Sự Cân Bằng Mới

Sự chuyển đổi này tạo ra một hợp đồng cân bằng:

Server:

  • Chấp nhận và xác thực dữ liệu từ form
  • Quản lý định tuyến
  • Trả về HTML hoặc JSON
  • Quản lý trạng thái và logic hiển thị

Frontend:

  • Xử lý nâng cao tương tác
  • Gửi form qua fetch()
  • Cập nhật các phần tử DOM một cách thông minh
  • Không tái hiện lại logic của server

Chúng ta không quay lại việc tải lại toàn bộ trang. Chúng ta chỉ để trình duyệt và server làm những gì mà chúng được thiết kế - với JavaScript lấp đầy những khoảng trống.


Ví Dụ Thực Tế Sử Dụng fetch()

Giả sử bạn có một form HTML nguyên bản. Thay vì dựa vào việc tải lại toàn bộ trang hoặc bọc nó trong logic của React hoặc Vue, bạn có thể nâng cấp nó như sau:

javascript Copy
function enhanceForm({
  formId,
  targetId,
  method = null,
  swap = "innerHTML",
  beforeSend = null,
  onSuccess = null,
  onError = null
}) {
  const form = document.getElementById(formId);
  const target = document.getElementById(targetId);

  if (!form || !target) return;

  form.addEventListener("submit", async function (e) {
    e.preventDefault();
    if (typeof beforeSend === "function") beforeSend(form);

    const formData = new FormData(form);
    const reqMethod = method || form.method || "POST";

    try {
      const response = await fetch(form.action, {
        method: reqMethod.toUpperCase(),
        headers: { 'X-Requested-With': 'fetch' },
        body: formData
      });

      const contentType = response.headers.get("content-type") || "";
      const result = contentType.includes("application/json")
        ? await response.json()
        : await response.text();

      if (response.ok) {
        if (typeof onSuccess === "function") onSuccess(result);
        else applySwap(target, result, swap);
      } else {
        if (typeof onError === "function") onError(result, response.status);
        else applySwap(target, `<strong>Lỗi:</strong> ${response.status}`, swap);
      }
    } catch (err) {
      if (typeof onError === "function") onError(err.message, 0);
      else applySwap(target, `<strong>Lỗi Mạng:</strong> ${err.message}`, swap);
    }
  });
}

function applySwap(target, content, swap) {
  switch (swap) {
    case "outerHTML": target.outerHTML = content; break;
    case "append": target.insertAdjacentHTML("beforeend", content); break;
    case "prepend": target.insertAdjacentHTML("afterbegin", content); break;
    case "innerHTML":
    default: target.innerHTML = content;
  }
}

enhanceForm("myForm", "result");

Giờ đây, server có thể phản hồi với HTML (hoặc JSON), và frontend chỉ cần thay thế nó vào DOM.


Server Thực Hiện Những Gì?

Trong mô hình này, server:

  • Xử lý các form đã gửi
  • Thực hiện xác thực
  • Hiển thị các đoạn HTML hoặc phản hồi JSON
  • Kiểm soát định tuyến và chuyển hướng
  • Lưu trữ dữ liệu

Dưới đây là cách mà điều đó có thể diễn ra trong Laravel:

php Copy
public function store(Request $request)
{
    $validated = $request->validate([
        'name' => 'required',
        'email' => 'required|email'
    ]);

    $user = User::create($validated);

    return view('partials.user-card', compact('user'));
}

Frontend sau đó sẽ chèn HTML này vào một phần cụ thể của trang.

Client Thực Hiện Những Gì?

Trong khi chúng ta đã giao lại nhiều trách nhiệm cho server, client vẫn đóng một vai trò thiết yếu - chỉ là không phải làm mọi thứ như trong thời kỳ SPAs.

Dưới đây là những gì mà trình duyệt chịu trách nhiệm bây giờ:

  • Nâng cao Giao Diện: Client nâng cao trải nghiệm người dùng khi cần thiết - như bật tắt modal, tự động định tâm đầu vào, hoặc xử lý cập nhật UI lạc quan. Nhưng không còn bị quá tải với việc hiển thị toàn bộ trang.

  • Gửi Form Qua JavaScript: Sử dụng fetch() hoặc các công cụ như enhanceForm(), client chặn các form đã gửi để: Ngăn chặn việc tải lại toàn bộ trang, Hiển thị chỉ báo tải, thay thế các đoạn HTML hoặc xử lý JSON.

  • Cập Nhật DOM: Thay vì phân biệt DOM ảo của React trên toàn bộ cây thành phần, client chỉ cập nhật các phần cụ thể của trang - như thay thế một danh sách, phần form, hoặc khu vực thông báo. Điều này chính xác và nhanh chóng.

  • Xử Lý Tương Tác Chỉ Có Client: Một số thứ vẫn thuộc về client - hãy nghĩ đến dropdowns, kéo và thả, phím tắt, và các bộ đếm trạng thái cục bộ. Những điều này không cần server.

  • Thực Hiện Các Yêu Cầu Dữ Liệu Bổ Sung: Cần thêm dữ liệu sau khi trang đã tải? Client vẫn có thể sử dụng fetch() để giao tiếp với APIs - chỉ là không bọc nó trong các thư viện trạng thái khổng lồ hoặc chuỗi hiệu ứng.


Các Stack Đang Làm Điều Này

Đây không chỉ là lý thuyết - các stack hiện đại đã bắt đầu áp dụng điều này.

Thế Giới JavaScript:

  • Remix - Nâng cao dần theo hình thức, tích hợp sẵn
  • SolidStart - Hiển thị nâng cao theo kiểu progressive-first
  • SvelteKit - Hiển thị phía server với nâng cao thông minh

Hệ Sinh Thái PHP:

  • Laravel Livewire - HTML-over-the-wire với logic backend
  • Inertia.js - Sử dụng Vue/React, nhưng được điều khiển bởi các định tuyến backend
  • HTMX - Nâng cao thuần HTML-over-the-wire
  • Alpine.js - Tương tác nhẹ trên HTML đã được hiển thị từ server

ASP.NET:

  • Blazor Server - Các thành phần điều khiển từ server với cập nhật DOM theo thời gian thực
  • Razor Pages + fetch - Rất gần với triết lý nâng cao theo hình thức này

Đâu Là Nơi Chúng Ta Đã Đến

Sau nhiều năm thử nghiệm và phức tạp hóa, chúng ta trở lại với một sự thật đơn giản hơn:

Trách Nhiệm Của Backend

  • Lưu trữ dữ liệu
  • Định tuyến
  • Xác thực
  • Logic kinh doanh
  • Hiển thị

Trách Nhiệm Của Frontend

  • Cập nhật DOM
  • Nâng cao form
  • Tương tác chọn lọc

Lời Kết

Chúng ta không đi một vòng tròn hoàn toàn - chúng ta đã tiến hóa. Từ các server đơn thể, đến SPAs nặng nề, và giờ đây là những ứng dụng thông minh, lai kết hợp sức mạnh của cả hai.

Kết quả? Các ứng dụng đơn giản hơn, nhanh hơn, dễ bảo trì hơn - và một web cảm giác như bản địa lần nữa.


Nếu bạn thấy bài viết này hữu ích, hãy xem xét việc hỗ trợ công việc của tôi - điều đó có ý nghĩa rất nhiều.

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào