如何在 Vue 中處理文字片段換行:4 種最佳方法

假設我們有一段文字:
    
const str = `這是第一行
這是第二行
這是第三行`
    

要使用 Vue 將其顯示非常的容易:
    
<div >
  {{ str }}
</div>
    

輸出結果:
    
這是第一行 這是第二行 這是第三行
    

咦? 等等,不對耶,換行呢?

原來 v-text 會將內容放到節點的 textContent 屬性中,並不會將內容解析為 HTML,所以不會換行,其實要顯示換行非常簡單:

方法一: v-html

將每行資料加上 <br/>,使用 v-html 能夠直接將原始資料顯示出來(放到 innerHTML 屬性中,會解析 HTML)。但是這種做法很可能會受到「跨站腳本攻擊(Cross-Site Scripting, XSS)」,因此不應該被使用,不過這確實也是一種方法。
    
const str = `這是第一行<br/>
這是第二行<br/>
這是第三行`
    
    
<div v-html="str">
</div>
    

方法二: 使用 CSS 的 white-space

white-space 有兩個屬性可以使用:
  • pre-line: 合併連續空白符號,但保留換行
  • break-spaces: 合併連續空白符號,但保留換行和其他空白符號
範例:
    
<div style="white-space: pre-line;">
  {{ str }}
</div>
    

方法三: 在 template 上執行 v-for 拆分內容和換行

有些時候不能使用 css (例如要將頁面輸出為其他格式時),那難道就只能使用 v-html 了嗎? 其實我們可以自己換行。
就是使用 <template> ,在裡面使用 v-for 將文字片段拆分為單行,在後面加入 <br/> 區塊
    
<template v-for="(line, index) in str.split('\n')" :key="index">
  {{ line }}<br/>
</template>
    

上面的程式碼有個小小的問題,就是最後一行後面會有個空白行,不過要解決也十分簡單:
    
<template v-for="(line, index) in str.split('\n')" :key="index">
  {{ line }}<br v-if="index !== str.split('\n').length - 1"/>
</template>
    

方法四: 使用 Vue 中的自定義指令

上面的方法會不會太簡單了,而且每次都要手動換很麻煩,有沒有厲害一點的方式?
當然!可以使用 Vue 的自定義指令(Custom Directives),只要加上 v-insert-linebreak 這個自定義指令,就可以在 Vue3 被掛載和更新時自動將換行符號替換為 <br/> 了!

註:本文示範的 Custom Directives 是使用 Vue3 的寫法:
    
const insertLinebreak = {
  mounted: (el) => {
    el.innerHTML = el.innerHTML.replace(/\n/g, '<br/>')
  }
  updated: (el) => {
    el.innerHTML = el.innerHTML.replace(/\n/g, '<br/>')
  },
}

export default {
  data() {
    return {
      str: `這是第一行
這是第二行
這是第三行`
    }
  },
  directives: {
    insertLinebreak,
  }
}
    
    
<div v-text="str" v-insert-linebreak>
</div>
    

漂亮,以後只要使用 v-insert-linebreak 就可以解決問題了,但,好像有程式碼重複了,未來要更改一次要改兩個位子的程式碼,能不能復用呢?
當然:
    
const insertLinebreak = {
  mounted: (el) => {
    insertLinebreak.updated(el);
  },
  updated: (el) => {
    el.innerHTML = el.innerHTML.replace(/\n/g, '<br/>')
  },
}
    
完美!

還有什麼其他方式呢?觀迎留言

參考資料:
mdn - Node.textContent
Vue.js - Built-in Directives
Vue.js - Custom Directives

留言