---
tags: VUE
---
【VUE】Vue元件|修飾符|指令
===
# 元件註冊
[文件](https://cn.vuejs.org/v2/guide/components-registration.html)
```javascript=
// 局部註冊
var ComponentA = { /* ... */ }
var ComponentB = {
components: {
'component-a': ComponentA
},
// ...
}
```
> 元件內資料與外層不同
# X-template
## 0. html
```htmlmixed=
<!-- 以下寫法tr不會被包在table>tbody中 -->
<table>
<tbody>
<row-component v-for="(item, key) in data" :person="item" :key="key"></row-component>
</tbody>
</table>
```
> 使用 is 掛載 template
```htmlmixed=
<!-- is寫法 改寫 -->
<tr is="row-component" v-for="(item, key) in data" :person="item" :key="key"></tr>
```
## 1. js
- Vue.component('元件名稱', {})
```htmlmixed=
<script type="text/x-template" id="rowCompointeTemplate">
<tr>
<td>{{ person.name }}</td>
<td>{{ person.cash }}</td>
<td>{{ person.icash }}</td>
</tr>
</script>
```
```javascript
Vue.component('row-component', {
props: ['person'],
template: '#rowCompointeTemplate'
})
```
## 2. 局部註冊
```javascript
var child = {
props: ['person'],
template: '#rowComponentTemplate'
}
var app = new Vue({
el: '#app',
components: {
"row-component": child
}
});
```
---
# Props: 由外而內傳入資料
- 單向數據流
```htmlmixed=
<script type="text/x-template" id="photo">
<div>
<img :src="imgUrl" class="img-fluid" alt="" />
<input type="text" class="form-control" v-model="newUrl">
</div>
</script>
```
```htmlmixed=
<script>
Vue.component('photo', {
props: ['imgUrl'],
template: '#photo',
data() {
return {
newUrl: this.imgUrl
}
}
})
</script>
```
- 資料匯入的時間差
- 利用v-if讓元件產生時間往後移,等資料匯入
```htmlmixed=
<card :user-data="user" v-if="user.phone"></card>
```
- 物件傳參考特性
- 維持狀態與生命週期
```htmlmixed=
<keep-alive>
<keep-card v-if="isShow">
</keep-card>
</keep-alive>
```
### props 型別
- 預設型別與預設值
```htmlmixed=
<script>
props: {
cash2: {
type: Number,
default: 200
}
},
</script>
```
- 靜態屬性
> 在靜態屬性傳數值時,傳入的為string
```htmlmixed=
<prop-type cash2="300"></prop-type>
<!-- 在靜態屬性傳數值時,傳入的為string -->
```
- 動態屬性
```htmlmixed=
<prop-type :cash2="300"></prop-type>
```
---
# emit: 向外層傳遞事件(Data)
```htmlmixed=
<div id="app">
<h2>透過 emit 向外傳遞資訊</h2>
我透過元件儲值了 {{ cash }} 元
<button-counter v-on:increment="incrementTotal"></button-counter>
<hr>
<button-counter v-on:increment="incrementTotal"></button-counter>
</div>
<script>
Vue.component('buttonCounter', {
template: `<div>
<button @click="incrementCounter" class="btn btn-outline-primary">增加 {{ counter }} 元</button>
<input type="number" class="form-control mt-2" v-model="counter">
</div>`,
data: function() {
return {
counter: 1
}
},
methods: {
incrementCounter() {
this.$emit('increment', Number(this.counter))
}
}
});
var app = new Vue({
el: '#app',
data: {
cash: 300
},
methods: {
incrementTotal(newNumber) {
this.cash += newNumber;
}
}
});
</script>
```
---
# Slot 插槽替換
#### 具名插槽
```htmlmixed=
<named-slot-component>
<header slot="header">替換的 Header</header>
<template>替換的 Footer</template>
<template slot="btn">按鈕內容</template>
<p>其餘的內容</p>
</named-slot-component>
<script type="text/x-template" id="namedSlotComponent">
<div class="card my-3">
<div class="card-header">
<slot name="header">這段是預設的文字</slot>
</div>
<div class="card-body">
<slot>
<h5 class="card-title">Special title treatment</h5>
<p class="card-text">With supporting text below as a natural lead-in to additional content.</p>
</slot>
<a href="#" class="btn btn-primary">
<slot name="btn">spanGo somewhere</slot>
</a>
</div>
<div class="card-footer">
<div>這是預設的 Footer</div>
</div>
</div>
Vue.component('named-slot-component', {
template: '#namedSlotComponent',
});
</script>
```
# is
### 使用is顯示單一組件
```htmlmixed=
<primary-component :data="item"></primary-component>
<!-- 與上方相同效果 -->
<div is="primary-component" :data="item"></div>
```
### 使用 is 動態切換組件
```htmlmixed=
<div :is="current" :data="item"></div>
```
```javascript=
Vue.component('primary-component', {
props: ['data'],
template: '#primaryComponent',
});
Vue.component('danger-component', {
props: ['data'],
template: '#dangerComponent',
});
var app = new Vue({
el: '#app',
data: {
item: {
header: '這裡是 header',
title: '這裡是 title',
text: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Enim perferendis illo reprehenderit ex natus earum explicabo modi voluptas cupiditate aperiam, quasi quisquam mollitia velit ut odio vitae atque incidunt minus?'
},
current: 'primary-component'
}
});
```
```htmlmixed=
<script type="text/x-template" id="primaryComponent">
<div class="card text-white bg-primary mb-3" style="max-width: 18rem;">
<div class="card-header">{{ data.header }}</div>
<div class="card-body">
<h5 class="card-title">{{ data.title }}</h5>
<p class="card-text">{{ data.text }}</p>
</div>
</div>
</script>
<script type="text/x-template" id="dangerComponent">
<div class="card text-white bg-danger mb-3" style="max-width: 18rem;">
<div class="card-header">{{ data.header }}</div>
<div class="card-body">
<h5 class="card-title">{{ data.title }}</h5>
<p class="card-text">{{ data.text }}</p>
</div>
</div>
</script>
```
# [vue] Vue 修飾符
## v-on: event
#### 事件修飾符
- .stop - 調用 event.stopPropagation()。
- .prevent - 調用 event.preventDefault()。
- .capture - 添加事件偵聽器時使用 capture 模式。
- .self - 只當事件是從偵聽器綁定的元素本身觸發時才觸發回調。
- .once - 只觸發一次回調。
```html
// example
@click.once="trigger('div')"
```
#### 按鍵修飾符
- .{keyCode | keyAlias} - 只當事件是從特定鍵觸發時才觸發回調。
- 別名修飾 - .enter, .tab, .delete, .esc, .space, .up, .down, .left, .right
- 修飾符來實現僅在按下相應按鍵時才觸發鼠標或鍵盤事件的監聽器 - .ctrl, .alt, .shift, .meta
```html
<input type="text" class="form-control" v-model="text" @keyup.shift.enter="trigger('shift + Enter')">
```
# [vue] Vue指令
### v-bind
- 用來綁定動態資料
- 可以省略,如 :href="link"
```html
<a v-bind:href="'https://' + link" target="_blank"> {{link}} </a>
<a :href="'https://' + link" target="_blank"> {{link}} </a>
```
### v-for, v-if
```html
<li v-for="(item, index) in list" v-if="item.age < 25">
{{ index+1 }}. {{ item.name }} 年齡是 {{ item.age }} 歲
</li>
```
### v-on
- 簡寫: @
- v-on 是一個事件監聽器
```html
<button class="btn btn-primary mt-1" v-on:click="reverseText">反轉字串</button>
```
```javascript
methods: {
reverseText() {
this.newText = this.text.split('').reverse().join('');
}
}
```
##### preventDefault()
```html
<a href="https://www.google.com.tw" v-on:click.prevent="reverseText" target="_blank" >反轉字串</a>
```
### 修飾符
[Event Handling — Vue.js](https://vuejs.org/v2/guide/events.html)
```html
<input type="text" v-model="text" v-on:keyup.enter="reverseText">
```
```html
<input type="text" v-model="text" @keyup.enter="reverseText">
<button @click.prevent="reverseText">反轉字串</button>
```
### v-class
- :class="{ '要加入的class': 判斷式 }"
- 傳入**物件**
- 多個class用空白隔開
- 多個判斷式用 ',', 如 { '要加入的class': 判斷式, '要加入的class': 判斷式 }
```html
<div class="box" :class="isTransform ? 'rotate':'' "></div>
<div class="box" :class="{ 'rotate abc': isTransform }"></div>
<div class="box" :class="{ 'rotate': isTransform, 'bg-danger': boxColor}"></div>
```
- 物件寫法
```
// html
:class="objectClass"
// js
objectClass: {
'rotate': false,
'bg-danger': false,
}
```
> 用在不確定要加上幾個className時
- 陣列寫法
```html
<button class="btn" :class="arrayClass">請操作本元件</button>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="classToggle3" v-model="arrayClass" value="btn-outline-primary">
<label class="form-check-label" for="classToggle3">切換樣式</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="classToggle4" v-model="arrayClass" value="active">
<label class="form-check-label" for="classToggle4">啟用元素狀態</label>
</div>
```
### :sytle
```html
<div class="box" :style="{'backgroundColor': 'red'}"></div>
<div class="box" :style="styleObject"></div>
<div class="box" :style="[{'backgroundColor': 'red'}, {'border': '10px solid #ccc'}]"></div>
```
- 陣列中插入object
```html
<div class="box" :style="[styleObject, styleObject2]"></div>
```
```javascript
// 使用駝峰式命名
styleObject: {
backgroundColor: 'red',
borderWidth: '5px'
},
styleObject2: {
boxShadow: '3px 3px 5px rgba(0, 0, 0, 0.16)'
}
```
> Vue在動態加入樣式時,會自動加上Prefix
## key
- 由於效能考量,在預設的狀況下,Vue.js 會儘量重覆使用已渲染好的元素。若不設定 key 值,不會重新渲染元素,只會部份更新。
- 綁定一個屬性 :key 並設定唯一值,目的是確保每個元素的唯一性,當元素更新,例如改變順序時,有可識別唯一性的 key 來確保重新渲染。
```html
<li v-for="(item, index) in list" :key="item.id">
{{index}} <input type="text" :placeholder="item.name" />
</li>
```
# 在組件上使用 v-model
[說明文](https://cn.vuejs.org/v2/guide/components.html#%E9%80%9A%E8%BF%87-Prop-%E5%90%91%E5%AD%90%E7%BB%84%E4%BB%B6%E4%BC%A0%E9%80%92%E6%95%B0%E6%8D%AE)
```htmlmixed=
<input v-model="searchText">
```
- 等價於
```htmlmixed=
<input
v-bind:value="searchText"
v-on:input="searchText = $event.target.value"
>
```
### 元件間 v-model 範例
```htmlmixed=
<custom-input
v-bind:value="searchText"
v-on:input="searchText = $event"
></custom-input>
<!-- or -->
<custom-input v-model="searchText"></custom-input>
```
```javascript=
Vue.component('custom-input', {
props: ['value'],
template: `
<input
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
`
})
```
- 客製 v-model
[參考說明](https://ithelp.ithome.com.tw/articles/10209183)
> v-model預設是用value當作prop,input當event,有時checkbox或radio或將value用在不同用途,因此用 "model" 可避免衝突
```javascript=
Vue.component('base-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
template: `
<input
type="checkbox"
v-bind:checked="checked"
v-on:change="$emit('change', $event.target.checked)"
>
`
})
```
```htmlmixed=
<base-checkbox v-model="lovingVue"></base-checkbox>
```