Vue3 單元 – 指令(Directives)
主題:v-html 與 v-text
簡介
在 Vue 3 中,指令(Directives) 是直接作用於 DOM 的特殊屬性,讓開發者可以在模板中以宣告式的方式改變元素的行為或內容。雖然 Vue 已提供了大量內建指令,最常被使用的還是 v-bind、v-model、v-if 等,但在處理文字與 HTML 內容時,v-html 與 v-text 扮演了不可或缺的角色。
v-html:把字串當作 HTML 解析並插入到元素內部。v-text:把字串作為純文字插入,等同於使用 Mustache ({{ }}) 但不會產生 HTML 解析的副作用。
了解這兩個指令的差異與正確使用時機,能讓我們在 防止 XSS(跨站腳本)攻擊、提升渲染效能以及維持程式碼可讀性方面受益良多。
核心概念
1. v-text – 純文字渲染
v-text 會把綁定的資料 直接寫入元素的 textContent,不會解析其中的 HTML 標籤。它的行為與 {{ message }} 完全相同,只是寫法上更明確且在某些情況下可避免 Mustache 產生的空白節點。
<div id="app">
<!-- 使用 Mustache -->
<p>{{ msg }}</p>
<!-- 使用 v-text -->
<p v-text="msg"></p>
</div>
// main.js
import { createApp } from 'vue'
createApp({
data() {
return {
msg: 'Hello <strong>Vue 3</strong>!'
}
}
}).mount('#app')
重點:
v-text會把<strong>這類標籤視為純文字顯示,畫面上會看到<strong>Vue 3</strong>,而不會加粗。
2. v-html – HTML 解析渲染
v-html 會把字串 作為 HTML 片段 插入到元素內部,等同於 innerHTML。這在需要顯示富文本(如 CMS 內容、Markdown 轉換結果)時非常便利。
<div id="app">
<div v-html="htmlContent"></div>
</div>
createApp({
data() {
return {
htmlContent: '<h2>Vue 3 教學</h2><p>使用 <em>v-html</em> 可以直接渲染 HTML。</p>'
}
}
}).mount('#app')
⚠️ 安全提醒:
v-html直接把字串注入 DOM,若來源不可信,極易成為 XSS 攻擊的入口。務必在插入前做好 內容過濾或消毒(如使用 DOMPurify)。
3. 為何還需要 v-text?
雖然 {{ }} 已能完成文字渲染,但在以下情況下 v-text 更具優勢:
| 場景 | 使用 {{ }} |
使用 v-text |
|---|---|---|
條件渲染 (v-if、v-show) 與文字同時出現時 |
可能產生空白節點 | 不會產生額外節點 |
| 大量文字更新(例如訊息列表) | 每次更新都會重新解析 Mustache | 直接更新 textContent,效能略佳 |
與其他指令混用(如 v-for + v-text) |
標記較繁雜 | 語意更清晰 |
4. 範例彙整
以下提供 5 個實用範例,涵蓋基本使用、動態更新、與安全過濾的完整流程。
範例 1:基本的 v-text 與 v-html 切換
<div id="app">
<button @click="useHtml = !useHtml">
切換 {{ useHtml ? '文字' : 'HTML' }}
</button>
<p v-if="useHtml" v-html="content"></p>
<p v-else v-text="content"></p>
</div>
createApp({
data() {
return {
useHtml: false,
content: '<span style="color:#42b983;">Vue 3 讓開發更快!</span>'
}
}
}).mount('#app')
說明:點擊按鈕切換顯示方式,能直觀感受
v-html解析標籤的差異。
範例 2:從 API 取得富文字,使用 DOMPurify 防止 XSS
<div id="app">
<h3>新聞內容</h3>
<article v-html="safeHtml"></article>
</div>
import DOMPurify from 'dompurify'
createApp({
data() {
return {
rawHtml: '' // 來自後端的未處理 HTML
}
},
computed: {
// 透過 computed 只在 rawHtml 改變時重新過濾
safeHtml() {
return DOMPurify.sanitize(this.rawHtml)
}
},
mounted() {
// 假設從遠端取得資料
fetch('https://api.example.com/news/123')
.then(res => res.text())
.then(html => {
this.rawHtml = html
})
}
}).mount('#app')
關鍵:
DOMPurify.sanitize能移除<script>、on*事件等危險屬性,確保插入的 HTML 安全。
範例 3:使用 v-text 顯示即時搜尋結果
<div id="app">
<input v-model="keyword" placeholder="輸入關鍵字" />
<p>搜尋結果:<span v-text="result"></span></p>
</div>
createApp({
data() {
return {
keyword: '',
items: ['Vue', 'React', 'Angular', 'Svelte']
}
},
computed: {
result() {
const found = this.items.filter(i => i.toLowerCase().includes(this.keyword.toLowerCase()))
return found.length ? found.join(', ') : '無符合項目'
}
}
}).mount('#app')
說明:
v-text直接更新文字,避免因{{ }}產生的空白節點干擾排版。
範例 4:在 v-for 中混用 v-html 與 v-text
<ul id="app">
<li v-for="(msg, idx) in messages" :key="idx">
<!-- 若訊息中包含 <b>,使用 v-html 否則使用 v-text -->
<span v-if="msg.includes('<b>')" v-html="msg"></span>
<span v-else v-text="msg"></span>
</li>
</ul>
createApp({
data() {
return {
messages: [
'普通文字',
'含有 <b>粗體</b> 的訊息',
'另一筆純文字'
]
}
}
}).mount('#app')
技巧:透過
v-if判斷字串是否含有 HTML 標籤,彈性選擇渲染方式。
範例 5:在表單中使用 v-html 顯示錯誤訊息(HTML 格式)
<div id="app">
<form @submit.prevent="handleSubmit">
<input v-model="email" type="email" placeholder="Email" />
<button type="submit">送出</button>
</form>
<!-- 錯誤訊息可能包含 <ul>、<li> 等結構 -->
<div v-if="errorMsg" class="error" v-html="errorMsg"></div>
</div>
createApp({
data() {
return {
email: '',
errorMsg: ''
}
},
methods: {
handleSubmit() {
// 簡易驗證示範
if (!this.email.includes('@')) {
this.errorMsg = `
<p>郵件格式錯誤,請檢查以下項目:</p>
<ul>
<li>必須包含 @ 符號</li>
<li>域名部分不能為空白</li>
</ul>`
} else {
this.errorMsg = ''
alert('表單送出成功!')
}
}
}
}).mount('#app')
提示:錯誤訊息直接以 HTML 呈現,讓開發者可以自訂樣式與結構,提升使用者體驗。
常見陷阱與最佳實踐
| 陷阱 | 可能產生的問題 | 解決方案 / 最佳實踐 |
|---|---|---|
直接使用 v-html 注入未經過濾的字串 |
XSS 攻擊、資料外洩 | 使用 DOMPurify、sanitize-html 等套件,或在後端先過濾 |
把大量 HTML 放在 data 中 |
造成 Vue 監控成本上升、效能下降 | 只在需要時才載入(例如懶加載),或使用 computed 產生 |
在同一元素同時使用 v-html 與其他屬性(如 v-bind:class) |
可能導致屬性被覆寫 | 確認 v-html 不會覆寫目標屬性,必要時分離成子元素 |
誤把 v-text 當作 v-html |
標籤被當成文字顯示,失去樣式 | 先確認需求是 純文字 還是 富文字,再選擇指令 |
使用 {{ }} 直接顯示 HTML |
會自動 escape,無法渲染 HTML | 改用 v-html(並確保安全) |
小技巧
封裝安全的 HTML 渲染元件
// SafeHtml.vue <template><div v-html="sanitized"></div></template> <script> import DOMPurify from 'dompurify' export default { props: ['rawHtml'], computed: { sanitized() { return DOMPurify.sanitize(this.rawHtml) } } } </script>讓所有使用者只需傳入
rawHtml,即可自動過濾。避免在
v-html中使用 Vue 插值 ({{ }})
Vue 只會在編譯階段處理模板,插入的 HTML 不會再被 Vue 解析,若想在 HTML 中使用資料,請先在 JavaScript 中組合字串。
實際應用場景
內容管理系統(CMS)
從後端取得的文章內容往往是 HTML,使用v-html搭配過濾即可直接渲染。即時聊天或評論功能
使用者可能貼上簡易的 Markdown,先轉成 HTML 再用v-html顯示,同時必須過濾腳本。表單驗證與錯誤提示
錯誤訊息需要列表或強調字體,v-html能讓錯誤樣式更彈性。動態產生的 SEO 友好文字
某些 SEO 需求需要在 meta 標籤內插入文字,使用v-text可避免不必要的 HTML 解析。多語系(i18n)文字渲染
若翻譯檔中包含<strong>、<br>等標籤,使用v-html可直接呈現,不必在程式碼中硬編碼。
總結
v-text:純文字渲染,等同於 Mustache,適合不需 HTML 解析的情況,能避免產生額外的空白節點,效能略佳。v-html:把字串當作 HTML 直接插入,適合顯示富文本,但必須 嚴格過濾 來源以防 XSS。- 在實務開發中,先判斷資料性質(純文字 vs 富文字),再選擇正確的指令,並配合 安全過濾、封裝元件 等最佳實踐,可讓 Vue 3 應用更安全、效能更佳、維護更容易。
掌握 v-html 與 v-text 的差異與使用時機,將為你的 Vue 專案增添彈性與安全性,讓開發者在面對各式文字與 HTML 需求時,都能游刃有餘。祝你在 Vue 3 的旅程中寫出更乾淨、更安全的程式碼!