# Xây dựng ứng dụng ngân hàng Phần 3: Các phương pháp lấy và sử dụng dữ liệu Hãy nghĩ về máy tính của Enterprise trong Star Trek - khi Captain Picard yêu cầu trạng thái của tàu, thông tin xuất hiện ngay lập tức mà không làm giao diện bị gián đoạn hay phải xây dựng lại toàn bộ. Dòng chảy thông tin mượt mà đó chính là điều chúng ta đang xây dựng ở đây với việc lấy dữ liệu động. Hiện tại, ứng dụng ngân hàng của bạn giống như một tờ báo in - cung cấp thông tin nhưng tĩnh. Chúng ta sẽ biến nó thành một thứ giống như trung tâm điều khiển tại NASA, nơi dữ liệu liên tục chảy và cập nhật theo thời gian thực mà không làm gián đoạn quy trình làm việc của người dùng. Bạn sẽ học cách giao tiếp với máy chủ một cách không đồng bộ, xử lý dữ liệu đến vào các thời điểm khác nhau và biến thông tin thô thành thứ có ý nghĩa đối với người dùng. Đây là sự khác biệt giữa một bản demo và phần mềm sẵn sàng cho sản xuất. ## ⚡ Những việc bạn có thể làm trong 5 phút tới **Lộ trình bắt đầu nhanh dành cho các nhà phát triển bận rộn** ```mermaid flowchart LR A[⚡ 5 minutes] --> B[Set up API server] B --> C[Test fetch with curl] C --> D[Create login function] D --> E[See data in action] ``` - **Phút 1-2**: Khởi động máy chủ API của bạn (`cd api && npm start`) và kiểm tra kết nối - **Phút 3**: Tạo một hàm cơ bản `getAccount()` sử dụng fetch - **Phút 4**: Kết nối biểu mẫu đăng nhập với `action="javascript:login()"` - **Phút 5**: Kiểm tra đăng nhập và xem dữ liệu tài khoản xuất hiện trong console **Lệnh kiểm tra nhanh**: ```bash # Verify API is running curl http://localhost:5000/api # Test account data fetch curl http://localhost:5000/api/accounts/test ``` **Tại sao điều này quan trọng**: Trong 5 phút, bạn sẽ thấy sự kỳ diệu của việc lấy dữ liệu không đồng bộ, điều này là nền tảng cho mọi ứng dụng web hiện đại. Đây là nền móng giúp ứng dụng trở nên phản hồi nhanh và sống động. ## 🗺️ Hành trình học tập của bạn qua các ứng dụng web dựa trên dữ liệu ```mermaid journey title From Static Pages to Dynamic Applications section Understanding the Evolution Traditional page reloads: 3: You Discover AJAX/SPA benefits: 5: You Master Fetch API patterns: 7: You section Building Authentication Create login functions: 4: You Handle async operations: 6: You Manage user sessions: 8: You section Dynamic UI Updates Learn DOM manipulation: 5: You Build transaction displays: 7: You Create responsive dashboards: 9: You section Professional Patterns Template-based rendering: 6: You Error handling strategies: 7: You Performance optimization: 8: You ``` **Điểm đến của hành trình**: Đến cuối bài học này, bạn sẽ hiểu cách các ứng dụng web hiện đại lấy, xử lý và hiển thị dữ liệu một cách động, tạo ra trải nghiệm người dùng mượt mà mà chúng ta mong đợi từ các ứng dụng chuyên nghiệp. ## Câu hỏi kiểm tra trước bài học [Câu hỏi kiểm tra trước bài học](https://ff-quizzes.netlify.app/web/quiz/45) ### Điều kiện tiên quyết Trước khi đi sâu vào việc lấy dữ liệu, hãy đảm bảo bạn đã chuẩn bị các thành phần sau: - **Bài học trước**: Hoàn thành [Biểu mẫu đăng nhập và đăng ký](../2-forms/README.md) - chúng ta sẽ xây dựng dựa trên nền tảng này - **Máy chủ cục bộ**: Cài đặt [Node.js](https://nodejs.org) và [chạy API server](../api/README.md) để cung cấp dữ liệu tài khoản - **Kết nối API**: Kiểm tra kết nối máy chủ của bạn với lệnh này: ```bash curl http://localhost:5000/api # Expected response: "Bank API v1.0.0" ``` Bài kiểm tra nhanh này đảm bảo tất cả các thành phần đang giao tiếp đúng cách: - Xác minh rằng Node.js đang chạy đúng trên hệ thống của bạn - Xác nhận rằng máy chủ API của bạn đang hoạt động và phản hồi - Kiểm tra rằng ứng dụng của bạn có thể kết nối với máy chủ (giống như kiểm tra liên lạc radio trước một nhiệm vụ) ## 🧠 Tổng quan về hệ sinh thái quản lý dữ liệu ```mermaid mindmap root((Data Management)) Authentication Flow Login Process Form Validation Credential Verification Session Management User State Global Account Object Navigation Guards Error Handling API Communication Fetch Patterns GET Requests POST Requests Error Responses Data Formats JSON Processing URL Encoding Response Parsing Dynamic UI Updates DOM Manipulation Safe Text Updates Element Creation Template Cloning User Experience Real-time Updates Error Messages Loading States Security Considerations XSS Prevention textContent Usage Input Sanitization Safe HTML Creation CORS Handling Cross-Origin Requests Header Configuration Development Setup ``` **Nguyên tắc cốt lõi**: Các ứng dụng web hiện đại là hệ thống điều phối dữ liệu - chúng phối hợp giữa giao diện người dùng, API máy chủ và mô hình bảo mật trình duyệt để tạo ra trải nghiệm mượt mà, phản hồi nhanh. --- ## Hiểu về việc lấy dữ liệu trong các ứng dụng web hiện đại Cách các ứng dụng web xử lý dữ liệu đã phát triển đáng kể trong hai thập kỷ qua. Hiểu được sự phát triển này sẽ giúp bạn đánh giá tại sao các kỹ thuật hiện đại như AJAX và Fetch API lại mạnh mẽ và tại sao chúng trở thành công cụ thiết yếu cho các nhà phát triển web. Hãy khám phá cách các trang web truyền thống hoạt động so với các ứng dụng động, phản hồi mà chúng ta xây dựng ngày nay. ### Ứng dụng nhiều trang truyền thống (MPA) Trong những ngày đầu của web, mỗi lần nhấp chuột giống như đổi kênh trên một chiếc TV cũ - màn hình sẽ trống rỗng, sau đó từ từ hiển thị nội dung mới. Đây là thực tế của các ứng dụng web ban đầu, nơi mỗi tương tác đều yêu cầu xây dựng lại toàn bộ trang từ đầu. ```mermaid sequenceDiagram participant User participant Browser participant Server User->>Browser: Clicks link or submits form Browser->>Server: Requests new HTML page Note over Browser: Page goes blank Server->>Browser: Returns complete HTML page Browser->>User: Displays new page (flash/reload) ``` ![Quy trình cập nhật trong ứng dụng nhiều trang](../../../../translated_images/mpa.7f7375a1a2d4aa779d3f928a2aaaf9ad76bcdeb05cfce2dc27ab126024050f51.vi.png) **Tại sao cách tiếp cận này cảm thấy cồng kềnh:** - Mỗi lần nhấp chuột đều yêu cầu xây dựng lại toàn bộ trang từ đầu - Người dùng bị gián đoạn giữa suy nghĩ bởi những lần nhấp chuột làm trang nhấp nháy - Kết nối internet của bạn phải làm việc quá sức để tải lại cùng một header và footer nhiều lần - Ứng dụng giống như việc lục lọi trong một tủ hồ sơ hơn là sử dụng phần mềm ### Ứng dụng một trang hiện đại (SPA) AJAX (Asynchronous JavaScript and XML) đã hoàn toàn thay đổi mô hình này. Giống như thiết kế mô-đun của Trạm Vũ trụ Quốc tế, nơi các phi hành gia có thể thay thế các thành phần riêng lẻ mà không cần xây dựng lại toàn bộ cấu trúc, AJAX cho phép chúng ta cập nhật các phần cụ thể của một trang web mà không cần tải lại toàn bộ. Mặc dù tên gọi có nhắc đến XML, chúng ta chủ yếu sử dụng JSON ngày nay, nhưng nguyên tắc cốt lõi vẫn giữ nguyên: chỉ cập nhật những gì cần thay đổi. ```mermaid sequenceDiagram participant User participant Browser participant JavaScript participant Server User->>Browser: Interacts with page Browser->>JavaScript: Triggers event handler JavaScript->>Server: Fetches only needed data Server->>JavaScript: Returns JSON data JavaScript->>Browser: Updates specific page elements Browser->>User: Shows updated content (no reload) ``` ![Quy trình cập nhật trong ứng dụng một trang](../../../../translated_images/spa.268ec73b41f992c2a21ef9294235c6ae597b3c37e2c03f0494c2d8857325cc57.vi.png) **Tại sao SPA cảm thấy tốt hơn:** - Chỉ những phần thực sự thay đổi mới được cập nhật (thông minh, đúng không?) - Không còn những gián đoạn khó chịu - người dùng của bạn vẫn giữ được dòng suy nghĩ - Ít dữ liệu di chuyển qua mạng hơn, nghĩa là tải nhanh hơn - Mọi thứ cảm thấy nhanh nhạy và phản hồi, giống như các ứng dụng trên điện thoại của bạn ### Sự phát triển đến Fetch API hiện đại Các trình duyệt hiện đại cung cấp [`Fetch` API](https://developer.mozilla.org/docs/Web/API/Fetch_API), thay thế [`XMLHttpRequest`](https://developer.mozilla.org/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest) cũ. Giống như sự khác biệt giữa việc vận hành một máy điện báo và sử dụng email, Fetch API sử dụng promises để viết mã không đồng bộ sạch hơn và xử lý JSON một cách tự nhiên. | Tính năng | XMLHttpRequest | Fetch API | |-----------|----------------|-----------| | **Cú pháp** | Phức tạp dựa trên callback | Sạch sẽ dựa trên promise | | **Xử lý JSON** | Yêu cầu phân tích thủ công | Phương pháp `.json()` tích hợp | | **Xử lý lỗi** | Thông tin lỗi hạn chế | Chi tiết lỗi toàn diện | | **Hỗ trợ hiện đại** | Tương thích với legacy | Promises và async/await của ES6+ | > 💡 **Khả năng tương thích trình duyệt**: Tin tốt - Fetch API hoạt động trên tất cả các trình duyệt hiện đại! Nếu bạn tò mò về các phiên bản cụ thể, [caniuse.com](https://caniuse.com/fetch) có câu chuyện đầy đủ về khả năng tương thích. > **Điểm mấu chốt:** - Hoạt động tốt trên Chrome, Firefox, Safari và Edge (về cơ bản là mọi nơi người dùng của bạn đang sử dụng) - Chỉ Internet Explorer cần hỗ trợ thêm (và thực sự, đã đến lúc để IE nghỉ ngơi) - Chuẩn bị hoàn hảo cho các mẫu async/await thanh lịch mà chúng ta sẽ sử dụng sau này ### Triển khai đăng nhập người dùng và lấy dữ liệu Bây giờ hãy triển khai hệ thống đăng nhập để biến ứng dụng ngân hàng của bạn từ một màn hình tĩnh thành một ứng dụng chức năng. Giống như các giao thức xác thực được sử dụng trong các cơ sở quân sự an toàn, chúng ta sẽ xác minh thông tin đăng nhập của người dùng và sau đó cung cấp quyền truy cập vào dữ liệu cụ thể của họ. Chúng ta sẽ xây dựng điều này từng bước, bắt đầu với xác thực cơ bản và sau đó thêm khả năng lấy dữ liệu. #### Bước 1: Tạo nền tảng cho hàm đăng nhập Mở tệp `app.js` của bạn và thêm một hàm `login` mới. Hàm này sẽ xử lý quy trình xác thực người dùng: ```javascript async function login() { const loginForm = document.getElementById('loginForm'); const user = loginForm.user.value; } ``` **Hãy phân tích điều này:** - Từ khóa `async`? Nó đang nói với JavaScript "này, hàm này có thể cần chờ đợi một số thứ" - Chúng ta đang lấy biểu mẫu từ trang (không có gì phức tạp, chỉ cần tìm nó bằng ID) - Sau đó, chúng ta lấy những gì người dùng đã nhập làm tên đăng nhập - Đây là một mẹo hay: bạn có thể truy cập bất kỳ đầu vào biểu mẫu nào bằng thuộc tính `name` của nó - không cần thêm các lệnh getElementById! > 💡 **Mẫu truy cập biểu mẫu**: Mỗi điều khiển biểu mẫu có thể được truy cập bằng tên của nó (được đặt trong HTML bằng thuộc tính `name`) như một thuộc tính của phần tử biểu mẫu. Điều này cung cấp một cách sạch sẽ, dễ đọc để lấy dữ liệu biểu mẫu. #### Bước 2: Tạo hàm lấy dữ liệu tài khoản Tiếp theo, chúng ta sẽ tạo một hàm chuyên dụng để lấy dữ liệu tài khoản từ máy chủ. Điều này tuân theo cùng một mẫu như hàm đăng ký của bạn nhưng tập trung vào việc lấy dữ liệu: ```javascript async function getAccount(user) { try { const response = await fetch('//localhost:5000/api/accounts/' + encodeURIComponent(user)); return await response.json(); } catch (error) { return { error: error.message || 'Unknown error' }; } } ``` **Đây là những gì mã này thực hiện:** - **Sử dụng** Fetch API hiện đại để yêu cầu dữ liệu không đồng bộ - **Xây dựng** URL yêu cầu GET với tham số tên đăng nhập - **Áp dụng** `encodeURIComponent()` để xử lý an toàn các ký tự đặc biệt trong URL - **Chuyển đổi** phản hồi sang định dạng JSON để dễ dàng thao tác dữ liệu - **Xử lý** lỗi một cách khéo léo bằng cách trả về một đối tượng lỗi thay vì làm ứng dụng bị crash > ⚠️ **Lưu ý bảo mật**: Hàm `encodeURIComponent()` xử lý các ký tự đặc biệt trong URL. Giống như các hệ thống mã hóa được sử dụng trong liên lạc hải quân, nó đảm bảo thông điệp của bạn đến đúng như dự định, ngăn các ký tự như "#" hoặc "&" bị hiểu sai. > **Tại sao điều này quan trọng:** - Ngăn các ký tự đặc biệt làm hỏng URL - Bảo vệ chống lại các cuộc tấn công thao túng URL - Đảm bảo máy chủ của bạn nhận được dữ liệu như dự định - Tuân theo các thực hành mã hóa an toàn #### Hiểu về yêu cầu HTTP GET Có một điều có thể làm bạn ngạc nhiên: khi bạn sử dụng `fetch` mà không có bất kỳ tùy chọn nào thêm, nó tự động tạo một yêu cầu [`GET`](https://developer.mozilla.org/docs/Web/HTTP/Methods/GET). Điều này hoàn hảo cho những gì chúng ta đang làm - yêu cầu máy chủ "này, tôi có thể xem dữ liệu tài khoản của người dùng này không?" Hãy nghĩ về yêu cầu GET như việc lịch sự yêu cầu mượn một cuốn sách từ thư viện - bạn đang yêu cầu xem một thứ đã tồn tại. Yêu cầu POST (mà chúng ta đã sử dụng cho đăng ký) giống như việc gửi một cuốn sách mới để thêm vào bộ sưu tập. | Yêu cầu GET | Yêu cầu POST | |-------------|-------------| | **Mục đích** | Lấy dữ liệu hiện có | Gửi dữ liệu mới đến máy chủ | | **Tham số** | Trong đường dẫn URL/chuỗi truy vấn | Trong nội dung yêu cầu | | **Bộ nhớ đệm** | Có thể được bộ nhớ đệm bởi trình duyệt | Không thường được bộ nhớ đệm | | **Bảo mật** | Hiển thị trong URL/nhật ký | Ẩn trong nội dung yêu cầu | ```mermaid sequenceDiagram participant B as Browser participant S as Server Note over B,S: GET Request (Data Retrieval) B->>S: GET /api/accounts/test S-->>B: 200 OK + Account Data Note over B,S: POST Request (Data Submission) B->>S: POST /api/accounts + New Account Data S-->>B: 201 Created + Confirmation Note over B,S: Error Handling B->>S: GET /api/accounts/nonexistent S-->>B: 404 Not Found + Error Message ``` #### Bước 3: Kết hợp tất cả lại với nhau Bây giờ đến phần thú vị - hãy kết nối hàm lấy dữ liệu tài khoản của bạn với quy trình đăng nhập. Đây là nơi mọi thứ hòa hợp: ```javascript async function login() { const loginForm = document.getElementById('loginForm'); const user = loginForm.user.value; const data = await getAccount(user); if (data.error) { return console.log('loginError', data.error); } account = data; navigate('/dashboard'); } ``` Hàm này tuân theo một trình tự rõ ràng: - Trích xuất tên đăng nhập từ đầu vào biểu mẫu - Yêu cầu dữ liệu tài khoản của người dùng từ máy chủ - Xử lý bất kỳ lỗi nào xảy ra trong quá trình - Lưu trữ dữ liệu tài khoản và điều hướng đến bảng điều khiển khi thành công > 🎯 **Mẫu Async/Await**: Vì `getAccount` là một hàm không đồng bộ, chúng ta sử dụng từ khóa `await` để tạm dừng thực thi cho đến khi máy chủ phản hồi. Điều này ngăn mã tiếp tục với dữ liệu chưa được xác định. #### Bước 4: Tạo nơi lưu trữ dữ liệu của bạn Ứng dụng của bạn cần một nơi để nhớ thông tin tài khoản sau khi nó được tải. Hãy nghĩ về điều này như bộ nhớ ngắn hạn của ứng dụng - một nơi để giữ dữ liệu của người dùng hiện tại. Thêm dòng này ở đầu tệp `app.js` của bạn: ```javascript // This holds the current user's account data let account = null; ``` **Tại sao chúng ta cần điều này:** - Giữ dữ liệu tài khoản có thể truy cập từ bất kỳ đâu trong ứng dụng của bạn - Bắt đầu với `null` nghĩa là "chưa ai đăng nhập" - Được cập nhật khi ai đó đăng nhập hoặc đăng ký thành công - Hoạt động như một nguồn sự thật duy nhất - không có sự nhầm lẫn về việc ai đang đăng nhập #### Bước 5: Kết nối biểu mẫu của bạn Bây giờ hãy kết nối hàm đăng nhập mới của bạn với biểu mẫu HTML. Cập nhật thẻ biểu mẫu của bạn như sau: ```html
``` **Điều mà thay đổi nhỏ này làm:** - Ngăn biểu mẫu thực hiện hành vi mặc định "tải lại toàn bộ trang" - Gọi hàm JavaScript tùy chỉnh của bạn thay thế - Giữ mọi thứ mượt mà và giống như ứng dụng một trang - Cho phép bạn kiểm soát hoàn toàn những gì xảy ra khi người dùng nhấn "Đăng nhập" #### Bước 6: Nâng cấp hàm đăng ký của bạn Để nhất quán, hãy cập nhật hàm `register` của bạn để cũng lưu trữ dữ liệu tài khoản và điều hướng đến bảng điều khiển: ```javascript // Add these lines at the end of your register function account = result; navigate('/dashboard'); ``` **Sự nâng cấp này cung cấp:** - **Chuyển đổi mượt mà** từ đăng ký đến bảng điều khiển - **Trải nghiệm người dùng nhất quán** giữa các luồng đăng nhập và đăng ký - **Truy cập ngay lập tức** vào dữ liệu tài khoản sau khi đăng ký thành công #### Kiểm tra triển khai của bạn ```mermaid flowchart TD A[User enters credentials] --> B[Login function called] B --> C[Fetch account data from server] C --> D{Data received successfully?} D -->|Yes| E[Store account data globally] D -->|No| F[Display error message] E --> G[Navigate to dashboard] F --> H[User stays on login page] ``` **Đã đến lúc thử nghiệm:** 1. Tạo một tài khoản mới để đảm bảo mọi thứ hoạt động 2. Thử đăng nhập với cùng thông tin đăng nhập đó 3. Xem console của trình duyệt của bạn (F12) nếu có gì đó không ổn 4. Đảm bảo bạn đến bảng điều khiển sau khi đăng nhập thành công Nếu có gì đó không hoạt động, đừng hoảng sợ! Hầu hết các vấn đề đều là những lỗi đơn giản như sai chính tả hoặc quên khởi động máy chủ API. #### Một lời về phép thuật Cross-Origin Bạn có thể đang tự hỏi: "Làm thế nào ứng dụng web của tôi nói chuyện với máy chủ API này khi chúng chạy trên các cổng khác nhau?" Câu hỏi hay! Điều này liên quan đến một điều mà mọi nhà phát triển web đều gặp phải cuối cùng. > 🔒 **Bảo mật Cross-Origin**: Các trình duyệt thực thi "chính sách cùng nguồn gốc" để ngăn chặn giao tiếp trái phép giữa các miền khác nhau. Giống như hệ thống kiểm tra tại Lầu Năm Góc, chúng xác minh rằng giao tiếp được ủy quyền trước khi cho phép chuyển dữ liệu. > **Trong thiết lập của chúng ta:** - Ứng dụng web của bạn chạy trên `localhost:3000` (máy chủ phát triển) - Máy chủ API của bạn chạy trên `localhost:5000` (máy chủ backend) - Máy chủ API bao gồm các [CORS headers](https://developer.mozilla.org/docs/Web/HTTP/CORS) cho phép giao tiếp từ ứng dụng web của bạn Cấu hình này phản ánh phát triển thực tế nơi các ứng dụng frontend và backend thường chạy trên các máy chủ riêng biệt. > 📚 **Tìm hiểu thêm**: Đi sâu hơn vào API và việc lấy dữ liệu với [module Microsoft Learn về API](https://docs.microsoft.com/learn/modules/use-apis-discover-museum-art/?WT.mc_id=academic-77807-sagibbon). ## Đưa dữ liệu của bạn vào HTML Bây giờ chúng ta sẽ làm cho dữ liệu được lấy hiển thị với người dùng thông qua thao tác DOM. Giống như quá trình phát triển ảnh trong phòng tối, chúng ta đang biến dữ liệu vô hình thành thứ mà người dùng có thể thấy và tương tác. Việc thao tác DOM là kỹ thuật biến các trang web tĩnh thành các ứng dụng động, cập nhật nội dung dựa trên tương tác của người dùng và phản hồi từ máy chủ. ### Chọn Công Cụ Phù Hợp Cho Công Việc Khi cần cập nhật HTML bằng JavaScript, bạn có nhiều lựa chọn. Hãy nghĩ đến chúng như các công cụ khác nhau trong hộp dụng cụ - mỗi công cụ phù hợp với một công việc cụ thể: | Phương pháp | Tốt cho việc gì | Khi nào sử dụng | Mức độ an toàn | |-------------|-----------------|-----------------|----------------| | `textContent` | Hiển thị dữ liệu người dùng một cách an toàn | Bất cứ khi nào bạn hiển thị văn bản | ✅ Rất an toàn | | `createElement()` + `append()` | Xây dựng bố cục phức tạp | Tạo các phần/lists mới | ✅ Cực kỳ an toàn | | `innerHTML` | Thiết lập nội dung HTML | ⚠️ Cố gắng tránh sử dụng | ❌ Rủi ro cao | #### Cách An Toàn Để Hiển Thị Văn Bản: textContent Thuộc tính [`textContent`](https://developer.mozilla.org/docs/Web/API/Node/textContent) là người bạn tốt nhất khi hiển thị dữ liệu người dùng. Nó giống như một người bảo vệ cho trang web của bạn - không có gì nguy hiểm có thể lọt qua: ```javascript // The safe, reliable way to update text const balanceElement = document.getElementById('balance'); balanceElement.textContent = account.balance; ``` **Lợi ích của textContent:** - Xử lý mọi thứ dưới dạng văn bản thuần túy (ngăn chặn việc thực thi script) - Tự động xóa nội dung hiện có - Hiệu quả cho việc cập nhật văn bản đơn giản - Cung cấp bảo mật tích hợp chống lại nội dung độc hại #### Tạo Các Phần Tử HTML Động Đối với nội dung phức tạp hơn, kết hợp [`document.createElement()`](https://developer.mozilla.org/docs/Web/API/Document/createElement) với phương thức [`append()`](https://developer.mozilla.org/docs/Web/API/ParentNode/append): ```javascript // Safe way to create new elements const transactionItem = document.createElement('div'); transactionItem.className = 'transaction-item'; transactionItem.textContent = `${transaction.date}: ${transaction.description}`; container.append(transactionItem); ``` **Hiểu cách tiếp cận này:** - **Tạo** các phần tử DOM mới một cách lập trình - **Kiểm soát** hoàn toàn thuộc tính và nội dung của phần tử - **Cho phép** cấu trúc phần tử lồng ghép phức tạp - **Bảo vệ** an toàn bằng cách tách biệt cấu trúc và nội dung > ⚠️ **Lưu ý về Bảo Mật**: Mặc dù [`innerHTML`](https://developer.mozilla.org/docs/Web/API/Element/innerHTML) xuất hiện trong nhiều hướng dẫn, nó có thể thực thi các script nhúng. Giống như các giao thức bảo mật tại CERN ngăn chặn việc thực thi mã không được phép, việc sử dụng `textContent` và `createElement` cung cấp các lựa chọn thay thế an toàn hơn. **Rủi ro của innerHTML:** - Thực thi bất kỳ thẻ `