\n\n\n`\n\nTrong ví dụ trên, biến `rawHtmlContent` chứa một chuỗi HTML hoàn chỉnh bao gồm thẻ `

` và `

` cùng với các thẻ định dạng như `` và ``. Khi Vue render component này, nội dung của `rawHtmlContent` sẽ được chèn trực tiếp vào `

` và hiển thị dưới dạng HTML thay vì văn bản thuần túy.\n\n### Các trường hợp sử dụng phổ biến\n\n`v-html` được sử dụng rộng rãi trong nhiều tình huống khác nhau:\n\n- **Hiển thị nội dung từ trình soạn thảo WYSIWYG (What You See Is What You Get):** Các trình soạn thảo như TinyMCE, CKEditor, QuillJS thường lưu trữ nội dung dưới dạng HTML. `v-html` là cách lý tưởng để hiển thị những nội dung này trong ứng dụng Vue của bạn.\n\n`\n data() {\n return {\n blogPostContent: '

Đây là nội dung bài viết được định dạng từ trình soạn thảo.

  • Mục 1
  • Mục 2
\"Ảnh'\n };\n }\n`\n`\n
\n`\n\n- **Hiển thị mô tả sản phẩm, bài viết:** Khi bạn nhận dữ liệu mô tả sản phẩm hoặc bài viết từ API backend, chúng thường có thể chứa các thẻ HTML để định dạng văn bản, thêm hình ảnh hoặc video nhúng.\n\n`\n // Giả sử dữ liệu nhận được từ API\n data() {\n return {\n productDescription: '

Sản phẩm này có chất liệu cao cấp và thiết kế hiện đại.


Xem thêm'\n };\n }\n`\n`\n
\n`\n\n- **Tích hợp nội dung từ bên thứ ba:** Đôi khi bạn cần nhúng các đoạn mã HTML từ các dịch vụ bên ngoài, ví dụ như mã nhúng video YouTube, biểu đồ từ thư viện bên ngoài, hoặc quảng cáo.\n\n`\n data() {\n return {\n youtubeEmbedCode: ''\n };\n }\n`\n`\n
\n`\n\n### Nguy cơ bảo mật và cách phòng tránh\n\n**Cảnh báo quan trọng:** `v-html` có thể dẫn đến các lỗ hổng bảo mật nghiêm trọng nếu không được sử dụng cẩn thận. Việc chèn HTML thô từ các nguồn không đáng tin cậy vào DOM có thể mở cửa cho các cuộc tấn công XSS.\n\n**XSS (Cross-Site Scripting)** là một loại tấn công bảo mật web cho phép kẻ tấn công chèn các đoạn mã độc (thường là JavaScript) vào trang web của bạn. Khi người dùng truy cập trang, mã độc đó sẽ được thực thi trong trình duyệt của họ, cho phép kẻ tấn công đánh cắp cookie, thông tin phiên, chuyển hướng người dùng đến các trang web độc hại, hoặc thậm chí chiếm quyền kiểm soát tài khoản của họ.\n\nVí dụ, nếu bạn nhận được một chuỗi HTML từ người dùng không được kiểm duyệt và chèn nó bằng `v-html`:\n\n`\n// DỮ LIỆU ĐỘC HẠI TỪ NGƯỜI DÙNG HOẶC API K","keywords":"VueJS","about":[{"@type":"Thing","name":"VueJS"}],"interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":0},"comment":[]}
TopDev

Cách Render HTML Thô Trong Vue.js Với v-html: Hướng Dẫn Chi Tiết

topdev 📖 10 phút đọc

Trong quá trình phát triển ứng dụng Vue.js, bạn thường xuyên làm việc với dữ liệu văn bản. Tuy nhiên, đôi khi bạn cần hiển thị nội dung HTML thô, chẳng hạn như nội dung từ trình soạn thảo rich-text, bài viết blog, hoặc dữ liệu được trả về từ API. Vue.js cung cấp một chỉ thị mạnh mẽ và tiện lợi để giải quyết vấn đề này: v-html. Bài viết này sẽ đi sâu vào cách sử dụng v-html, các trường hợp áp dụng, và những lưu ý quan trọng để đảm bảo an toàn cho ứng dụng của bạn.

v-html là gì và tại sao cần sử dụng?#

Theo mặc định, Vue.js sẽ thoát (escape) mọi nội dung HTML được chèn vào để ngăn chặn các cuộc tấn công kịch bản chéo trang (XSS - Cross-Site Scripting). Điều này có nghĩa là nếu bạn có một chuỗi HTML như <h1>Hello</h1>, Vue sẽ hiển thị nó dưới dạng văn bản <h1&gt;Hello&lt;/h1&gt; thay vì render một tiêu đề h1. Đây là một cơ chế bảo mật quan trọng, nhưng đôi khi lại gây bất tiện khi bạn muốn hiển thị HTML đúng nghĩa.

v-html là một chỉ thị đặc biệt trong Vue.js cho phép bạn chủ động render nội dung HTML thô vào DOM. Khi bạn sử dụng v-html="yourHtmlString", Vue sẽ lấy giá trị của yourHtmlString và chèn nó trực tiếp vào phần tử đó dưới dạng HTML đã được phân tích cú pháp. Điều này rất hữu ích khi bạn đang làm việc với các nội dung đã được định dạng trước và cần hiển thị chúng nguyên trạng.

Cách sử dụng v-html#

Sử dụng v-html cực kỳ đơn giản. Bạn chỉ cần gắn chỉ thị này vào một phần tử HTML và gán cho nó một biến chứa chuỗi HTML bạn muốn hiển thị.

Ví dụ cơ bản:

`

`

Trong ví dụ trên, biến rawHtmlContent chứa một chuỗi HTML hoàn chỉnh bao gồm thẻ <h2><p> cùng với các thẻ định dạng như <strong><code>. Khi Vue render component này, nội dung của rawHtmlContent sẽ được chèn trực tiếp vào <div> và hiển thị dưới dạng HTML thay vì văn bản thuần túy.

Các trường hợp sử dụng phổ biến#

v-html được sử dụng rộng rãi trong nhiều tình huống khác nhau:

  • Hiển thị nội dung từ trình soạn thảo WYSIWYG (What You See Is What You Get): Các trình soạn thảo như TinyMCE, CKEditor, QuillJS thường lưu trữ nội dung dưới dạng HTML. v-html là cách lý tưởng để hiển thị những nội dung này trong ứng dụng Vue của bạn.

data() { return { blogPostContent: '<p>Đây là nội dung bài viết được định dạng từ trình soạn thảo.</p><ul><li>Mục 1</li><li>Mục 2</li></ul><img src="/images/my-image.jpg" alt="Ảnh minh họa">' }; } <div class="blog-content" v-html="blogPostContent"></div>

  • Hiển thị mô tả sản phẩm, bài viết: Khi bạn nhận dữ liệu mô tả sản phẩm hoặc bài viết từ API backend, chúng thường có thể chứa các thẻ HTML để định dạng văn bản, thêm hình ảnh hoặc video nhúng.

// Giả sử dữ liệu nhận được từ API data() { return { productDescription: '<p>Sản phẩm này có <strong>chất liệu cao cấp</strong> và thiết kế hiện đại.</p><br><a href="#">Xem thêm</a>' }; } <div class="product-description" v-html="productDescription"></div>

  • Tích hợp nội dung từ bên thứ ba: Đôi khi bạn cần nhúng các đoạn mã HTML từ các dịch vụ bên ngoài, ví dụ như mã nhúng video YouTube, biểu đồ từ thư viện bên ngoài, hoặc quảng cáo.

data() { return { youtubeEmbedCode: '<iframe width="560" height="315" src="https://www.youtube.com/embed/dQw4w9WgXcQ" frameborder="0" allowfullscreen></iframe>' }; } <div class="video-player" v-html="youtubeEmbedCode"></div>

Nguy cơ bảo mật và cách phòng tránh#

Cảnh báo quan trọng: v-html có thể dẫn đến các lỗ hổng bảo mật nghiêm trọng nếu không được sử dụng cẩn thận. Việc chèn HTML thô từ các nguồn không đáng tin cậy vào DOM có thể mở cửa cho các cuộc tấn công XSS.

XSS (Cross-Site Scripting) là một loại tấn công bảo mật web cho phép kẻ tấn công chèn các đoạn mã độc (thường là JavaScript) vào trang web của bạn. Khi người dùng truy cập trang, mã độc đó sẽ được thực thi trong trình duyệt của họ, cho phép kẻ tấn công đánh cắp cookie, thông tin phiên, chuyển hướng người dùng đến các trang web độc hại, hoặc thậm chí chiếm quyền kiểm soát tài khoản của họ.

Ví dụ, nếu bạn nhận được một chuỗi HTML từ người dùng không được kiểm duyệt và chèn nó bằng v-html:

// DỮ LIỆU ĐỘC HẠI TỪ NGƯỜI DÙNG HOẶC API KHÔNG ĐÁNG TIN CẬY // KHÔNG NÊN DÙNG TRỰC TIẾP NHƯ THẾ NÀY! const maliciousHtml = "<img src='x' onerror='alert("Bạn đã bị hack!")'>";

Khi chuỗi này được render, hàm onerror sẽ được kích hoạt, chạy đoạn mã JavaScript độc hại.

Để giảm thiểu rủi ro XSS, hãy luôn tuân thủ các nguyên tắc sau:

  • Chỉ sử dụng v-html với các nguồn đáng tin cậy: Đây là nguyên tắc quan trọng nhất. Nếu bạn không kiểm soát được nguồn gốc của HTML hoặc không thể xác minh tính an toàn của nó (ví dụ: dữ liệu nhập từ người dùng không được kiểm duyệt, API công cộng không rõ nguồn gốc), TUYỆT ĐỐI KHÔNG sử dụng v-html.

  • Kiểm duyệt (Sanitize) HTML ở phía Server: Cách tốt nhất để đảm bảo an toàn là kiểm duyệt HTML trên backend trước khi gửi nó đến frontend. Backend nên sử dụng các thư viện kiểm duyệt HTML chuyên dụng (ví dụ: DOMPurify cho Node.js, HTML Purifier cho PHP, Bleach cho Python) để loại bỏ tất cả các thẻ và thuộc tính nguy hiểm có thể chứa mã độc hại. Điều này giúp loại bỏ nguy cơ ở cấp độ nguồn dữ liệu.

    Thư viện gợi ý: JavaScript (Node.js): DOMPurify (có thể dùng cả frontend và backend) - PHP: HTML Purifier - Python: Bleach - Ruby: Loofah

  • Sử dụng thư viện kiểm duyệt HTML ở phía Client (nếu không thể kiểm duyệt ở Server): Nếu vì lý do nào đó bạn không thể kiểm duyệt HTML trên server, bạn có thể cân nhắc sử dụng các thư viện kiểm duyệt như DOMPurify ở phía client trước khi gán nó vào biến mà v-html sử dụng. Tuy nhiên, việc kiểm duyệt ở phía client ít an toàn hơn vì mã có thể bị thao túng.

    Ví dụ sử dụng DOMPurify:

npm install dompurify

`

<script>
import DOMPurify from 'dompurify';

export default {
  name: 'App',
  data() {
    return {
      // Giả định đây là HTML nhận được từ nguồn không đáng tin cậy
      rawInputHtml: '<img src=x onerror=alert("XSS Attack!")><p>Safe content.</p>',
      sanitizedHtmlContent: ''
    };
  },
  created() {
    // Tiến hành kiểm duyệt trước khi gán vào biến
    this.sanitizedHtmlContent = DOMPurify.sanitize(this.rawInputHtml);
  }
}
</script>

Trong ví dụ này,DOMPurify.sanitize()sẽ loại bỏ thuộc tínhonerror` độc hại, chỉ giữ lại phần HTML an toàn.

  • Hạn chế quyền của v-html: Tránh đặt v-html trong các phần tử có thể được sử dụng để tương tác trực tiếp với người dùng (ví dụ: input, textarea) nếu không có lý do chính đáng và kiểm soát chặt chẽ.

Các lưu ý khác khi sử dụng v-html#

  • Không thể biên dịch Vue template bên trong v-html: v-html chỉ đơn giản là chèn chuỗi HTML vào DOM. Nó không biên dịch bất kỳ chỉ thị Vue nào (ví dụ: v-bind, v-on, {{ }}) hoặc component con bên trong nội dung được chèn. Nếu bạn cần các tính năng tương tác của Vue, bạn cần suy nghĩ về việc tái cấu trúc dữ liệu để sử dụng các component Vue phù hợp.

    Ví dụ:

data() { return { htmlWithVueSyntax: '<button @click="showAlert">Click me</button>' }; } methods: { showAlert() { alert('Button clicked!'); } } <!-- Nút này SẼ KHÔNG hoạt động vì @click không được biên dịch --> <div v-html="htmlWithVueSyntax"></div>

Nếu bạn cần render các component hoặc có logic tương tác bên trong nội dung HTML động, bạn có thể cần xem xét các giải pháp phức tạp hơn như:
Sử dụng Vue's `render` function để tạo VNodes động.

- Sử dụng `<component :is="dynamicComponent">` nếu nội dung động của bạn là tên component.

- Sử dụng thư viện bên ngoài như `vue-runtime-dom` hoặc `vue-template-compiler` (trong môi trường Node.js) để biên dịch template HTML thành component Vue động, nhưng điều này phức tạp và không được khuyến khích cho các trường hợp đơn giản.
  • CSS Scoped và v-html: Nếu bạn sử dụng scoped CSS trong các component Vue, các style này sẽ không áp dụng cho nội dung được chèn bởi v-html. Điều này là do scoped CSS chỉ tác động lên các phần tử được tạo ra bởi cùng một component. Các phần tử được chèn bởi v-html được coi là bên ngoài phạm vi của component đó.

    Để style nội dung bên trong v-html, bạn cần: Sử dụng global CSS (không có scoped).

    • Sử dụng các CSS selector cụ thể và mạnh mẽ trong component cha để ghi đè (ví dụ: >>> hoặc /deep/ combinator, mặc dù chúng đang bị deprecated, hoặc sử dụng ::v-deep pseudo-element trong Vue 3).

    • Đặt nội dung v-html trong một component con riêng và áp dụng CSS không scoped cho component con đó.

`

<script>
export default {
  data() {
    return {
      htmlContent: '<span>Đây là một đoạn HTML có thể cần style.</span><p>Đoạn văn này có màu đỏ?</p>'
    }
  }
}
</script>

<style scoped>
.my-component h3 {
  color: blue;
}

/* CSS này sẽ KHÔNG áp dụng cho <p> bên trong v-html */
.my-component p {
  color: red;
}

/* Cách dùng ::v-deep (Vue 3) hoặc >>> (Vue 2, deprecated) */
.raw-content ::v-deep p {
  color: red; /* Điều này sẽ hoạt động */
}
</style>

`

  • Hiệu suất: Đối với các chuỗi HTML rất lớn hoặc khi v-html được cập nhật thường xuyên, có thể có một chút ảnh hưởng đến hiệu suất do việc phân tích cú pháp và chèn DOM. Tuy nhiên, trong hầu hết các trường hợp sử dụng thông thường, điều này không phải là vấn đề đáng lo ngại.

Kết luận#

v-html là một công cụ mạnh mẽ và cần thiết trong Vue.js để hiển thị nội dung HTML thô. Nó đơn giản hóa đáng kể việc tích hợp các nội dung phức tạp từ các nguồn khác nhau. Tuy nhiên, sức mạnh đi kèm với trách nhiệm. Việc sử dụng v-html một cách thiếu cẩn trọng có thể tạo ra những lỗ hổng bảo mật nghiêm trọng. Luôn ưu tiên kiểm duyệt HTML trên phía server và chỉ sử dụng v-html với các nguồn dữ liệu đáng tin cậy đã được kiểm tra kỹ lưỡng. Nắm vững các nguyên tắc này sẽ giúp bạn xây dựng các ứng dụng Vue.js mạnh mẽ, linh hoạt và an toàn.

Bài liên quan trong #VueJS

✓ Đã sao chép link