Vue 基本指令大全
===
###### tags: `vue筆記`

以下紀錄最常用的幾個 vue 基本指令:
[v-text](#v-text)、[v-for](#v-for)、[v-if](#條件判斷v-if)、[v-bind](#HTML屬性綁定v-bind)、[v-model](#v-model)、[v-on](#v-on事件觸發)
# v-text
把資料從JS以純文字方式渲染到畫面上,可以使用 `v-text` 標籤或是雙大括號`{{}}` ,一般開發上會使用 `{{}}`,最為方便 。
在 HTML 中想要渲染資料的位置上輸入雙大括號即可:
```html
<div id="app">
<p>{{name}}在{{position}}</p>
</div>
```
```jsx=
const App = {
data(){
return {
name:'Jordan',
position:'公司',
}
}
};
Vue.createApp(App).mount('#app')
```
那一般人會想說既然用雙大括弧很方便,那還要標籤方式用來幹嘛?
*標籤可以在另外改樣式啊!* 以下面的程式碼為例,把 `name` 放在 `span` 標籤中,就可以把樣式獨立出來。
```html
<div id="app">
<p>{{name}}在{{position}}</p>
<p><span class="text-big" v-text="name"></span>在{{position}}</p>
</div>
```
前面資料上有一個 `rawHTML` ,是一段 html。以過去使用經驗會出現在用戶建立自己文章、產品、資訊頁面...等。
但為了安全性,渲染到畫面上會是以字串形式,所以如果直接用 `{{rawHTML}}` ,會把p標籤也帶出來,這時候可以用 `v-html` 。但是因為避免被攻擊,所以最好少用這個:
```html
<div v-html="rawHTML"></div>
```
## v-once
因為Vue是雙向綁定,所以用 {{}} 綁定的資料,如果資料庫的資料更新時,畫面也會即時進行更新。但有時候會有某些資料不想要在用戶重新整理畫面前會因為就被變更的話(ex:改用戶名字,會想儲存後才變更),就可以加入 v-once 標籤,表示只會渲染一次,不會即時重新渲染:
```html
<div id="app">
<p v-once>{{name}}在{{position}}</p>
</div>
```
## 其他進階技巧
1. 樣板字面值:前面把兩個資料放在一起要寫兩個 `{{}}` ,但其實可以直接使用樣板字面值,如同JS一樣方法撰寫更清晰:
```html
<div id="app">
<p >{{ ${name} 在 ${position} }}</p>
</div>
```
2. 反轉字串:可以用原生JS方法,reserve()
```html
<div id="app">
<p >{{ ${name}.split('').reserve().join('') }}</p>
</div>
```
3. 使用方法:可以用 methods 創造不同的內容,像是底下的 `say()` 函式,帶入不同參數回傳不同內容:
```html
<div id="app">
<p >{{ say('Jordan')}}</p>
</div>
```
```jsx
const App = {
data(){
return {
name:'Jordan',
position:'公司',
rowHTML:'<p>Hello world</p>'
}
},
methods:{
say(name){
return `${name}在${this.position}`
}
}
};
Vue.createApp(App).mount('#app')
```
4. JS運算:可以直接在{{}} 中做運算
```html
<div id="app">
<p>{{1+1}}</p>
</div>
```
5. 想要顯示{{}}:透過 `v-pre`,可以保留{{}},不會進行轉譯:
```html
<div id="app">
<p v-pre>{{name}}在{{position}}吃早餐</p>
</div>
```

---
# v-for
v-for,就像原生 for 迴圈,過去在JS要先把HTML用迴圈組好,才放到HTML中。
而 Vue 透過 `v-for` 可以直接在畫面中建立迴圈把陣列資料或是物件屬性,按照順序執行,先假設陣列中有兩個物件:
```jsx
const App = {
data() {
return {
products: [
{
name: '蛋餅',
price: 30,
vegan: false
},
{
name: '飯糰',
price: 35,
vegan: false
}
],
}}};
```
在 html,我們先用`ul`、 `li` 方式呈現,可以在 li 中加入 v-for,並用 [item.name](http://item.name) 的方式抓出物件屬性。
```html
<ul>
<li v-for="(item, key) in products">
{{ key }} - {{ item.name}} / {{ item.price }} 元
</li>
</ul>
```
- item:陣列中的每一個物件
- key:陣列中的位置數值
- products:陣列名稱
看起來非常完美,但是有一件事特別要注意,要在 v-for 後面加上 `:key="唯一值"`,可以為其加上 `v-bind:key` ,因為當渲染的資料做位置調整時,Vue不會對一樣的元素做調整,同時也要避免使用index,因為當陣列資料調整順序時,陣列index也會隨之調整。
```html
<ul>
<li v-for="(item, key) in products" :key="item.name">
{{ key }} - {{ item.name}} / {{ item.price }} 元
</li>
</ul>
```
所以如果在外層加上 `v-bind:key="獨一無二的值"` ,這樣裡面的東西都會被視為獨一無二,當被移動時,也會跟著走~
---
**如果要顯示的是物件呢?**
```jsx
const App = {
data() {
return {
productsObj: {
chineseOmelette:{
name: '蛋餅',
price: 30,
vegan: false
},
riceBall:{
name: '飯糰',
price: 35,
vegan: false
}
},
}}};
```
其實也是一樣做法喔~只是 key 會變成物件屬性名稱!
```html
<ul>
<li v-for="(item, key) in productsObj">
{{ key }} - {{ item.name}} / {{ item.price }} 元
</li>
</ul>
```
另外也可以直接純數值回圈:
```html
<ul>
<li v-for="i in 6">
{{ i }}元
</li>
</ul>
```
進階技巧:可以多加一個 template 標籤搭配 v-for。
為啥要這樣做呢?因為像下面情況,無法加在 tbody 上,也不能加在 tr,那我們就自己創造一個新標籤吧~~
```html
<table class="table">
<tbody>
<template v-for="(item, key) in products" :key="item.name">
<tr>
<th rowspan="2">{{ key + 1 }}</th>
<td colspan="2">
{{ item.name }}
</td>
</tr>
<tr>
<td>
{{ item.price }}元
</td>
<td>
{{ item.vegan? '可素食': '不可素食' }}
</td>
</tr>
</template>
</tbody>
</table>
```
---
# 條件判斷v-if
v-if 就是利用資料的 true 或 false 做判斷,來控制 html 元素。
可以在要顯示的標籤上加上 v-if = "資料名稱"。如果 true,就會顯示,false就會刪除
```html
<div id="app">
<p v-if="isHome">{{ ${name} 在 ${position} }}</p>
</div>
```
## if、else if 、else
既然是 if,當然也能像一般 js 一樣做其他判斷呀~~這個很酷!
以下面為例,假如資料有一個是今天誰放假,那根據誰放假會顯示他們今天要幹嘛,剩下兩位都只能上班。
```html
<div>
<p v-if="takeOff === '小明'">小明吃早餐</p>
<p v-else-if="takeOff === '小美'">小美去百貨公司</p>
<p v-else-if="takeOff === '杰倫'">杰倫去幫助人</p>
<p>其他人都上班</p>
</div>
```
另外也能搭配按鈕做連動喔~~
官方說:`v-if` 和 `v-for` 不建議混用~但如果真的要用,就不要把這兩個放在同一個標籤上面。可以利用 template 標籤作為 `v-for`,而 `<li>` 就可以寫 `v-if`。
以下面的城市範例:當 `item.price > 20` 才會渲染。
```html
<ul>
<template v-for="(item, key) in products" v-bind:key="item.name">
<li v-if="item.price > 20">
{{ key }} - {{ item.name}} / {{ item.price }} 元
<input type="text">
</li>
</template>
</ul>
```
### v-if 和 v-show 差異
v-if:是完全的條件渲染,條件會根據監聽被刪除或建立,可以像是下面的樣式直接註解掉,是不存在的。

*如果初始渲染時候條件是 false,就不會做任何事情,直到提件第一次成立時,才會開始渲染。
v-show:相比 `v-if` , `v-show` 很簡單就只是 css 上的隱藏,`display:none`,所以不論一開始條件為何,都會渲染,只是先隱藏起來而已。
### 那要用哪一個呢?
對於效能來說,`v-if` 在切換時候會消耗效能,而 `v-show` 是在一開始渲染時消耗效能。所以如果該部件是需要頻繁的切換,建議用 `v-show` 比較好。如果很少會改變條件的話,就用 `v-if` 。
---
`v-bind:` 可以對 html 標間屬性與 js 資料做連動,像底下本來這麼長一大串圖片連結,
```html
<img src="https://images.unsplash.com/photo-1600182610361-4b4d664e07b9?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=200&q=80" class="square-img" alt="">
```
`v-bind:src="屬性名稱"` 就可以替換成資料庫中的特定資料,同時可以做到即時更新:
```html
<img v-bind:src="product.imgUrl" class="square-img" alt="">
```
如果資料有建立完整,甚至還能以該資料名稱當作圖片 title
```html
<img v-bind:src="product.imgUrl" class="square-img" alt="" v-bind:title="product.name">
```
縮寫:`v-bind:`打起來很長,所以可以直接使用縮寫方式綁定,把 v-bind 去掉,保留`:`就好
```html
<img :src="product.imgUrl" class="square-img" alt="" :title="product.name">
```
---
# HTML屬性綁定v-bind
`v-bind:` 也是超常使用的功能是在,可以綁定在任何 html 標籤的屬性上面,以`<img>`標籤為例,可以直接綁定資料庫的 `url` 到 `src` 屬性中:
```html
<img v-bind:src="imgUrl" v-bind:title="imgName">
```
v-bind有縮寫,就是直接把 v-bind去掉,只保留冒號
```html
<img :src="imgUrl" :title="imgName">
```
## 更多屬性綁定
基本上所有可以想到的HTML屬性都可以綁定,像是 disable,我們再以下面的範例是`v-bind:disabled="名稱"` ,這個名稱就是對應資料 isFull 的布林值,如果是 true 就是disabled。
```html
<form action="">
<input type="text" value="我要吃蘿蔔糕">
<button type="submit" :disabled="isFull">送出</button>
</form>
<button type="button" v-on:click="change('isFull')">狀態切換</button>
```
另外我最常搭配 `v-for`:如果搭配上迴圈渲染,那就是改成item,同時可以看到增加在 input 標籤和 label 標籤中,也能直接使用資料獨一無二的值來當作配對基準,就不用另外想了。
```html
<table class="table">
<tbody>
<tr v-for="item in products" :key="item.imgUrl">
<td>
<img :src="item.imgUrl" class="square-img" alt="" :title="item.name">
</td>
<td>
{{ item.name }}
</td>
<td>
{{ item.price }}元
</td>
<td>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" :id="item.name">
<label class="form-check-label" :for="item.name">
我要這個
</label>
</div>
</td>
</tr>
</tbody>
</table>
```
## 動態綁定
原本的 `v-bind:`,是直接綁定原本的資料,但是也能透過搭配三元運算子做動態綁定。這個案例也是冒號,但要加上中括弧`:[名稱]`就可以透過改變該值來做傳遞資料:
```html
<button type="button"
v-on:click="dynamic = dynamic === 'disabled' ? 'readonly':'disabled'">
切換為 {{ dynamic }}</button>
<input type="text" :value="name" :[dynamic]>
```
假設資料庫有一筆變數是 dynamic,我們透過click來切換true或false,所以當點擊按鈕時,dynamic是'disabled',就轉換成readonly,如果不是就等於disabled。
## 樣式綁定
另外 `v-bind:` 不只可以綁定標籤屬性,也能綁定樣式,像是以我們最常用的class,`v-bind:class` ,我一般會以物件形式包裹,以下面的一個`<p>`當作簡單範例,當資料中的`is_ok`變成true時,文字就會變色:
```html
<p class="subTxt" :class="{text-primary:is_ok}">Hello World</p>
```
課程中老師提到的範例,透過按鈕切換`isTransform` 和`boxColor` 的布林值:
```html
<div id="app">
<div class="box" :class="{ rotate: isTransform, 'bg-danger': boxColor }"></div>
<button class="btn btn-outline-primary"
v-on:click="change('isTransform')">選轉物件</button>
<button class="btn btn-outline-primary ms-1"
v-on:click="change('boxColor')">切換色彩</button>
</div>
```
- 他的key值是對應className,物件值是判斷式
- 如果class名稱有-,記得加上引號,因為物件屬性不能有-。如:'bg-danger'
以上面HTML範例,同時加入一個方法是可帶入參數,讓參數值相反
```jsx
const App = {
data() {
return {
isTransform: true,
boxColor: false,
objectClass: {
'rotate': false,
'bg-danger': false,
},
};
},
methods: {
change: function (key) {
this[key] = !this[key];
},
},
};
```
上面code有一個 `objectClass` 以物件包裹好幾個class,就是可以直接放一包,就不用一個一個帶入。在HTML中就只要放入該屬性名稱即可:
```html
<div class="box" :class="objectClass"></div>
```
另外也能用陣列方式帶入多個className,另外如果是用陣列方式,資料端就不用寫布林值:
```jsx
const App = {
data() {
return {
arrayClass: [''],
};
},
methods: {
addClass(arr) {
this.arrayClass.push(...arr);
},
},
};
```
上面code案例中可以看到一個 `arrayClass` 空陣列,我們目標就是操作函式把class推送進去 `arrayClass` 陣列中,然後同時帶入 html :
```html
<button class="btn" :class="arrayClass">請操作本元件</button>
<button type="button"class="btn btn-outline-primary"
v-on:click="addClass(['btn-primary', 'active'])">為陣列加入 Class</button>
```
## :style
也能寫成inlinestyle方式,先以最簡單的直接寫來看看(key帶入style屬性,值帶入style相對應的值):
```html
<div class="box" :style="{backgroundColor: red}"></div>
```
其實就跟一般的寫法一樣,只是前面是`:style` ,當然他也能把值改成資料庫的內容,這樣就能隨時改顏色:
```html
<div class="box" :style="{backgroundColor:styleObject.backgroundColor}}"></div>
```
另外也能像`:class` 一樣,整個帶入資料:
```html
<div class="box" :style="styleObject"></div>
<div class="box" :style="[styleObject, styleObject2]"></div>
```
```jsx
const App = {
data() {
return {
styleObject2: {
boxShadow: '3px 3px 5px rgba(0, 0, 0, 0.16)'
},
styleObject3: {
userSelect: 'none'
}
arrayClass: [''],
};
},
methods: {
addClass(arr) {
this.arrayClass.push(...arr);
},
},
};
```
---
# v-model
v-model 是應用在表單元件或自訂元件上(例:`<input>`、`<select>`和`<textarea>`),可以用來跟 data 的雙向綁定。
我們先假設以這段 data,接下來可以在 input 綁定 v-model,來跟 data 做即時綁定:
```jsx
const App = {
data() {
return {
name: '小明',
text: '一段文字敘述',
checkAnswer: false,
checkAnswer2: '',
checkAnswer3: [],
radioAnswer: '蛋餅',
selectAnswer: '',
selectAnswer2: [],
}
},
};
```
搭配前面的 `v-text` 練習,把 `data.name` 直接渲染在畫面上,而 綁定了 `v-model="name"` 的`<input>` 一開始會先顯示 `data.name` 的值,當用戶更改 `<input>`,`data.name` 的值也會跟著改變,這就是雙向綁定。
```html
<input type="text" v-model="name" >
<p>{{ name }}</p>
```
---
## checkbox
checkbox,如果只有一個就是單選框,這時候 v-model 是用 data 的布林值判斷,當 true 就會打勾,同時前面 v-text 也能用三元運算子, true 就帶出正面答案。
```html
<p>小明,你是吃飽沒?</p>
<p>{{ checkAnswer ? '吃飽了' : '還沒'}}</p>
<div>
<input type="checkbox" id="check1" v-model="checkAnswer">
<label for="check1">小明回覆</label>
</div>
```

如果不想用三元運算值的話,也能把 true 和 false 的值寫在 input 屬性中:
```html
<p>小明,你是吃飽沒?</p>
<p>{{ checkAnswer2 }}</p>
<div>
<input type="checkbox" id="check2" v-model="checkAnswer2" true-value="吃飽了" false-value="還沒">
<label for="check2">小明回覆</label>
</div>
```
**其實這個很實用,用於想要回傳給data的不是布林值,而是自定義值時候就可以改成這種`true-value="吃飽了" false-value="還沒"`作法**
---
## 多選checkbox
單選很實用,多選也很實用喔,多選不同的是用陣列儲存資料,當被勾選時,v-model 就會把value 帶進該陣列中:
```html
<p>你還要吃什麼?</p>
<p>{{ checkAnswer3.join(' ') }}</p>
<div>
<input type="checkbox" v-model="checkAnswer3" id="check3" value="蛋餅" >
<label for="check3">蛋餅</label>
</div>
<div>
<input type="checkbox" v-model="checkAnswer3" id="check4" value="蘿蔔糕" >
<label for="check4">蘿蔔糕</label>
</div>
<div>
<input type="checkbox" v-model="checkAnswer3" id="check5" value="豆漿" >
<label for="check5">豆漿</label>
</div>
```
- 要存成陣列的話,注意每個 inpu t的 v-modal 都要寫一樣,而 id 和 value 可以不同。
- 這裡的 `{{ checkAnswer3.join(' ') }}` 是說把陣列每筆資料中間用空格串連轉成文字 ~~

---
## 單選 radio
radio雖然也有很多選項,但是他是單選,所以 data 不是以陣列形式,是以字串方式,當選到另外一個選項時,該值就會覆蓋原值:
```html
<p>你還要吃什麼?</p>
<p>{{ radioAnswer }}</p>
<div>
<input type="radio" v-model="radioAnswer" id="radio1" value="蛋餅" >
<label for="radio1">蛋餅</label>
</div>
<div>
<input type="radio" v-model="radioAnswer" id="radio2" value="蘿蔔糕" >
<label for="radio2">蘿蔔糕</label>
</div>
<div>
<input type="radio" v-model="radioAnswer" id="radio3" value="豆漿" >
<label for="radio3">豆漿</label>
</div>
```
---
## select下拉選單
先從單選說起,單選可以搭配 v-for 把 data中的特定陣列資料渲染成一個一個 option 選項:
```html
<p>你還要吃什麼?</p>
<p>{{ selectAnswer }}</p>
<select class="form-select" v-model="selectAnswer">
<option value="" disabled selected>說吧,你要吃什麼?</option>
<option :value="item.name" v-for="item in products"
:key="item.name">{{item.name}}/{{item.price}}元</option>
</select>
```
- "說吧,你要吃什麼?"那行有加上 disabled selected 兩個屬性是為了預設先顯示預設選項,同時選了之後就不能在選他了,必須選一個正確的選項。
- option 最常搭配 v-for 迴圈顯示 data 資料,所以也記得要加上 :key 獨一無二的值,以及最重要的 :vlaue。
```jsx
const App = {
data() {
return {
selectAnswer: '',
selectAnswer2: [],
products: [
{
name: '蛋餅',
price: 30,
vegan: false
},
{
name: '飯糰',
price: 35,
vegan: false
},
{
name: '小籠包',
price: 60,
vegan: false
},
{
name: '蘿蔔糕',
price: 30,
vegan: true
},
],
}
},
};
```
## select 多選
多選跟單選基本上程式碼幾乎一樣,只需要在加上 multiple 即可:
```html
<p>你還要吃什麼?</p>
<p>{{ selectAnswer2 }}</p>
<select class="form-select" multiple v-model="selectAnswer2">
<option value="" disabled selected>說吧,你要吃什麼?</option>
<option :value="item.name" v-for="item in products"
:key="item.name">{{item.name}}/{{item.price}}元</option>
</select>
```
---
# v-on事件觸發
v-on,就是類似原生JS中的 `addEventListener()` ,所以 v-on 後面可以接各種觸發機制,像是click、change... 搭配各種 methods。
因為 v-on很常用,所以最好用縮寫就是 `@click`、`@submit`、`@change`
## on-click
最萬用的事件觸發語法,最基礎是搭配`<button>`,但其實我也會搭配`<div>`,`<p>`,做成點擊卡片或卡片中的名字之類的。
```html
<button type="button" class="btn btn-primary" @click="newProduct">
新增產品</button>
```
用在卡片時候,可以如果卡片本體和裡面的元素都有v-on事件的話,加上修飾符`.stop`,避免互相干擾:
```html
<div @click="openProduct(product.id)">
<p>產品名稱</p>
<p>卡片名稱</p>
<button type="button" class="btn btn-primary" @click.stop="edit(product.id)">
編輯</button>
</div>
```
以下列出不同修飾符,常用的就是前三個吧~
- .stop : 停止觸發上層 DOM 元素事件。
- .prevent : 避免瀏覽器預設行為。
- .capture : 不管觸發事件的目標是否是下層, 設定 capture 的事件一定會先觸發。
- .self : 只有觸發此 DOM 元素本身才會觸發 self 事件。
- .once : 此事件只觸發一次。
- .passive : 無視 prevent 功能。
## on-change
可以搭配`<input>`標籤,當修改值並且網頁焦點離開 `input` 後,就會觸發 change 事件。
```html
<div id="nameVue">
<input id="name" v-model="name" @change="checkName" />
<span style="color:red">{{alertMessage}}</sapn>
</div>
```
但其實 vue 本身就有的方法可以監聽資料更改時執行其他事件,就是 watch 而且更即時,所以 @change 我也不是太常用。
## on-submit
`form` 表單一般都有 submit 事件,可以同時搭配驗證表單套件。但表單 submit 會有原生傳送的事件,所以也利用上面說的修飾符避免預設行為`@sunmit.prevent`。