跳至主要内容

[Vue] Vue指令-渲染畫面

TL;DR

Vue指令大致分為三類:雙向綁定、畫面渲染、事件觸發。

這邊主要介紹渲染畫面使用的指令。

v-text & {{}}

v-text{{}}(mustache)的使用上基本上是相同的,都是為了渲染純文字使用的。v-text需要搭配html標籤使用。

都是直接在裡面插入data內的變數。

使用方法如下:

<p>{{name}}{{position}}吃早餐</p>
<p><span v-text="name"></span><span v-text="position"></span>吃早餐</p>

開發者工具查看渲染畫面

使用v-text的好處是可以將span修改成strong的標籤,就可以改為粗體。斜體也是一樣的。

data雙向綁定

如果今天data的值與一個input雙向綁定,則畫面也會同步修改。

<p>{{name}}{{position}}吃早餐</p>
<p><span v-text="name"></span><span v-text="position"></span>吃早餐</p>
<input type="text" v-model="name">
<input type="text" v-model="position">

修改input的內容也會導致畫面更動

單次綁定(v-once)

如果只希望畫面在初始化那次要渲染data的內容,但是之後data變動時畫面不希望更動。就可以使用v-once指令。 使用方法:

<p>{{name}}{{position}}吃早餐</p>
<p><span v-text="name"></span><span v-text="position"></span>吃早餐</p>
<p v-once><span v-text="name"></span><span v-text="position"></span>吃早餐</p>

v-once不需要帶入任何參數。接著不管如何變更data內的資料,畫面都不會更動。

渲染出{{}}

如果希望可以在畫面中顯示出{{}},不希望被判讀成mustache將內容轉譯出來,可以使用v-pre的指令:

<p v-pre>加上v-pre的{{}}不會被轉譯</p>

{{}} 進階技巧

其實{{}}內可以放入任何的表達式。我們可以利用表達式撰寫靈活的程式碼:

<div id="app">
<div class="p-5">
<h3>進階技巧:表達式</h3>
<p>{{name}}{{position}}吃早餐</p>
<p>{{`${name}${position}吃早餐`}}</p>
<p>{{`${name}${position}${30+50+100}元的早餐`}}</p>
<p>{{text.split('').reverse().join('')}}</p>
<p>可以綁定methods: {{say('杰倫')}}</p>
<p>{{ 87+87 }}</p>
<hr>
<h3>進階技巧:顯示資料狀態</h3>
<p>{{products}}</p>
</div>
</div>
  1. 樣板字面值(Line:4 ~ Line:7)
    因為在{{}}內是撰寫表達式,所以我們可以撰寫一個樣板字面值。使用樣板字面值我們可以直接使用$去取得變數內容。另外因為${}內也是表達式,所以也可以直接做計算。

  2. js原生方法(Line:7)
    只要是表達是都可以撰寫在{{}}內,所以我們可以使用js原生的方法,將字串拆解,反轉,再組合。結果會輸出 "餐早吃店餐早在明小"

  3. 調用methods內的方法(Line:8)
    在前面有事先定義一個方法:

    methods: {
    say(name) {
    return `${name}${this.position}吃早餐`
    }
    }

    所以我們在{{}}內可以呼叫這個function,會將我們所傳入的字串杰倫組合成新的字串後return,並且渲染出來。

  4. 數值計算(Line:9) 既然都可以撰寫表達式了,數值的計算也是一個表達式。會將計算結果回傳出來並且渲染。

  5. 顯示資料狀態
    開發過程中雖然可以透過套件去觀察data的內容,但是我們也可以直接將data的值透過{{}}顯示在畫面上。開發結束後再將其移除。

v-html

為了防止XSS攻擊,在data內的資料透過v-text or {{}} 的方式都只會渲染為字串

如果要將資料以html標籤的方式解析,則需要使用v-html的指令:

<div id="app">
<div class="p-5">
{{rawHtml}}
<p v-text="rawHtml"></p>
<p v-html="rawHtml"></p>
</div>
</div>

指令不同,渲染的方式也不同

Line:5 正確解析為html的格式。

注意事項

官方文件有提到:

在你的站点上动态渲染任意的 HTML 是非常危险的,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要将用户提供的内容作为插值

可以的話最好不要使用v-html指令

v-for

當我們需要渲染多筆資料的時候,就會使用v-forv-for可以帶入兩個參數:第一個是內容本身,第二個是索引值(從0開始)。如果不需要索引值可以只帶入一個參數。

另外我們也可以使用:key="`${item.id}-list`"的方式,來組合成一組字串。

key

這邊的key不是我們在v-for內自定義的名稱key。而是vue定義的。 我們每次使用v-for的時候都需要加上v-bind:key="唯一值",用以區別不同的元件。

這是因為vue的運作機制的關係,有相同父元素的子元素必須有獨特的 key。重複的 key 會造成渲染錯誤。

索引值當成key可不可行?

雖然理論上來說,索引值也會是唯一值。但是因為vue在渲染v-for的元素時是採用就地更新的方式,如果使用索引值當作key在元素需要調整順序時會有效能上的問題。所以本身是不建議利用索引值當作:key內的值的。

但是如果元素的排序並不會更動,就不會造成效能上的問題。

可以參考此篇it幫系列文

簡單來說,可以的話還是使用id之類不會變動的值當作唯一值是最好的

陣列

v-for使用時會依序取出陣列內的內容,以以下範例來說就是每個品項的物件。item代表物件本身,是可以自行命名的,第二個參數可以選用,用法類似forEach。

利用v-for渲染多筆陣列內的資料:

<div id="app">
<div class="p-5">
<h3>呈現多筆資料於畫面上 v-for</h3>
<p>菜單</p>
<ul>
<li v-for="(item,key) in products" v-bind:key="item.name">
{{key+1}} - {{item.name}} / {{item.price}}
</li>
</ul>
</div>
</div>

v-for迴圈陣列渲染結果

key是item的索引值,值預設從0開始,我們希望從1開始,可以設定為{{key + 1}}

物件

使用v-for迴圈將物件渲染到畫面上時

item內容會是屬性的值(value)
key則會是屬性名稱

<div id="app">
<div class="p-5">
<h3>呈現多筆資料於畫面上 v-for</h3>
<h4>物件回圈</h4>
<p>菜單</p>
<ul>
<li v-for="(item,key) in productsObj" v-bind:key="item.name">
{{key}} - {{item.name}} / {{item.price}}
</li>
</ul>
</div>
</div>

v-for物件迴圈範例結果

因為這邊的key是屬性名稱,所以沒有辦法+1(應該說這邊如果+1則會變成字串的相加)

純數值

v-for也可以用在純數值上,item會依序從1開始到指定的數字。

也可以使用巢狀迴圈:

<div id="app">
<div class="p-5">
<ul>
<li v-for="item in 5" :key="item">
{{item}}:<span v-for="item in 10" :key="item">{{item}}</span>
</li>
</ul>
</div>
</div>

v-for純數值迴圈

template

官方文件

有時候想要回圈渲染的內容,受限於html標籤沒有辦法直接使用一組v-for就將內容給渲染出來。這時候可以使用template標籤來一次渲染包含多個元素的區塊。

<div id="app">
<div class="p-5">
<ul>
<h3>進階技巧:在 template 標籤使用 v-for</h3>
<table class="table">
<tbody>
<template v-for="(item,key) in products" :key="item.name">
<tr >
<th rowspan="2">{{key}}</th>
<td colspan="2">
{{item.name}}
</td>
</tr>
<tr>
<td>
{{item.price}}
</td>
<td>
{{item.vegan?"素食":"非素食"}}
</td>
</tr>
</template>
</tbody>
</table>
</ul>
</div>
</div>

另外這邊使用了三元運算子,判斷vegan內的布林值,並且回傳出不同的字串。

v-if / v-else

v-if指令可以讓我們判斷要不要顯示該節點,另外可以使用v-else去判斷例外狀況:

<div id="app">
<div class="p-5">
<h3>v-if</h3>
<p v-if="isFull">小明 飽了</p>
<p v-else>小明 還沒吃飽</p>
<button type="button" v-on:click="change('isFull')">狀態切換</button>
</div>
</div>
v-if與v-for的混用

官方文件建議v-ifv-for不要撰寫在同一個元素上。因為優先級會不明顯。同時使用時v-if會優先執行。

如果真的有需要同時使用,可以利用template來使用v-for,內容再搭配v-if判斷

<template v-for="">
<p v-if></p>
</template>

v-else-if

透過onclick切換link的內容,顯示目前套用active的是哪個按鈕。並且根據link內容切換要渲染哪個段落。

<div id="app">
<div class="p-5">
<h3>v-else-if</h3>
<nav class="nav nav-pills nav-fill">
<a class="nav-link" href="#" v-bind:class="{ 'active': link === '小明' }"
v-on:click="link = '小明'">小明</a>
<a class="nav-link" href="#" v-bind:class="{ 'active': link === '小美' }"
v-on:click="link = '小美'">小美</a>
<a class="nav-link" href="#" v-bind:class="{ 'active': link === '杰倫' }"
v-on:click="link = '杰倫'">杰倫</a>
</nav>

<div>
<div v-if="link === '小明'">小明吃早餐</div>
<div v-else-if="link === '小美'">小美去百貨公司</div>
<div v-else>杰倫去幫助人</div>
</div>
</div>
</div>

v-if / v-else-if / v-else 是有順序性的。如果把 Line:15 & Line:16 互換,會沒有辦法正確顯示

v-show

v-showv-if使用方法類似,但是差異在讓節點消失的原理不同。
v-show是利用display:none讓節點消失;v-if則是移除整個節點。

<h3>v-if 與 v-show</h3>
<p v-show="isFull">小明 飽了</p>
<p v-if="isFull">小明 飽了</p>
<button type="button" v-on:click="change('isFull')">狀態切換</button>
  • 顯示時
    v-if and v-show 顯示狀態

  • 不顯示時
    v-if and v-show 不顯示狀態