---
# System prepended metadata

title: 【VUE】Vue元件|修飾符|指令
tags: [VUE]

---

---
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>
```