# 多個 元素 / 元件 的轉場
>[竹白記事本](https://chupainotebook.bloggi.co/)
[Vue Transition|目錄](https://hackmd.io/@chupai/HkQQA8c4U)
###### tags: `Vue Transition` `竹白的 Vue.js 學習筆記`
## 多個元素的轉場
`<transition>` 也可以用在多個元素的轉場。
舉例來說:
```htmlmixed
<transition>
<table v-if="items.length > 0">
<!-- ... -->
</table>
<p v-else>Sorry, no items found.</p>
</transition>
```
但要注意,Vue 在切換畫面時,並不會完全置換元素,會使用一種最大限度減少動態元素並且儘可能的嘗試修復/再利用相同類型元素的算法。
因此當有相同標籤的元素在做切換時,需要使用 `key` 特性設置唯一的值來標記以讓 Vue 區分它們。
```htmlmixed
<transition>
<button v-if="isEditing" key="save">
Save
</button>
<button v-else key="edit">
Edit
</button>
</transition>
```
在一些場景中,也可以通過給同一個元素的 key 特性設置不同的狀態來代替 `v-if` 和 `v-else`,上面的範例可以重寫為:
```htmlmixed
<transition>
<button v-bind:key="isEditing">
{{ isEditing ? 'Save' : 'Edit' }}
</button>
</transition>
```
使用多個 `v-if` 的多個元素的轉場可以重寫為綁定了動態屬性的單個元素轉場。例如:
```htmlmixed
<transition>
<button v-if="docState === 'saved'" key="saved">
Edit
</button>
<button v-if="docState === 'edited'" key="edited">
Save
</button>
<button v-if="docState === 'editing'" key="editing">
Cancel
</button>
</transition>
```
可以重寫為:
```htmlmixed
<!-- .html -->
<transition>
<button :key="docState">
{{ buttonMessage }}
</button>
</transition>
```
```javascript
// ...
computed: {
buttonMessage: function () {
switch (this.docState) {
case 'saved': return 'Edit'
case 'edited': return 'Save'
case 'editing': return 'Cancel'
}
}
}
```
### 1. 轉場模式
請考慮以下程式碼:
```htmlmixed
<transition>
<button :key="isEditing" @click="isEditing = !isEditing">
{{ isEditing ? 'On' : 'Off' }}
</button>
</transition>
```
<iframe height="200" style="width: 100%;" scrolling="no" title="Vue CSS 轉場 轉場模式 問題" src="https://codepen.io/CHUPAIWANG/embed/ZEYpreQ?height=265&theme-id=default&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href='https://codepen.io/CHUPAIWANG/pen/ZEYpreQ'>Vue CSS 轉場 轉場模式 問題</a> by CHUPAIWANG
(<a href='https://codepen.io/CHUPAIWANG'>@CHUPAIWANG</a>) on <a href='https://codepen.io'>CodePen</a>.
</iframe>
在 on 按鈕和 off 按鈕的轉場中,兩個按鈕都被重繪了,一個離開轉場的時候另一個開始進入轉場。這是 `<transition>` 的預設行為,進入和離開同時發生。
你可能會想到,可以加上 `position: absolute`,因為兩者重疊了,所以不會產生元素位移的情況:
```css
button {
position: absolute;
}
```
<iframe height="200" style="width: 100%;" scrolling="no" title="Vue CSS 轉場 轉場模式 絕對定位" src="https://codepen.io/CHUPAIWANG/embed/zYxKRzo?height=265&theme-id=default&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href='https://codepen.io/CHUPAIWANG/pen/zYxKRzo'>Vue CSS 轉場 轉場模式 絕對定位</a> by CHUPAIWANG
(<a href='https://codepen.io/CHUPAIWANG'>@CHUPAIWANG</a>) on <a href='https://codepen.io'>CodePen</a>.
</iframe>
但如果加上 `translate` 讓它們運動像滑動,還是會有兩個元素同時出現的破綻:
```css
.v-enter,
.v-leave-to {
opacity: 0;
transform: translateX(100%);
}
```
<iframe height="200" style="width: 100%;" scrolling="no" title="Vue CSS 轉場 轉場模式 絕對定位 + 滑動" src="https://codepen.io/CHUPAIWANG/embed/abzmqyW?height=265&theme-id=default&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href='https://codepen.io/CHUPAIWANG/pen/abzmqyW'>Vue CSS 轉場 轉場模式 絕對定位 + 滑動</a> by CHUPAIWANG
(<a href='https://codepen.io/CHUPAIWANG'>@CHUPAIWANG</a>) on <a href='https://codepen.io'>CodePen</a>.
</iframe>
同時生效的進入和離開的轉場不能滿足所有要求,所以 Vue 提供了轉場模式:
- `in-out`:新元素先進行轉場,完成之後當前元素轉場離開。
- `out-in`:當前元素先進行轉場,完成之後新元素轉場進入。
加上 `mode="out-in"` 的效果:
```htmlmixed
<transition mode="out-in">
<button :key="isEditing" @click="isEditing = !isEditing">
{{ isEditing ? 'On' : 'Off' }}
</button>
</transition>
```
```css
.v-enter-active, .v-leave-active {
transition: 1.5s opacity;
}
```
<iframe height="265" style="width: 100%;" scrolling="no" title="Vue CSS 轉場 轉場模式 out-in" src="https://codepen.io/CHUPAIWANG/embed/vYEXdWr?height=265&theme-id=default&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href='https://codepen.io/CHUPAIWANG/pen/vYEXdWr'>Vue CSS 轉場 轉場模式 out-in</a> by CHUPAIWANG
(<a href='https://codepen.io/CHUPAIWANG'>@CHUPAIWANG</a>) on <a href='https://codepen.io'>CodePen</a>.
</iframe>
`in-out` 模式的範例:
```css
button {
position: absolute;
}
.v-enter,.v-leave-to {
opacity: 0;
}
.v-enter {
transform: translateX(100%);
}
.v-leave-to {
transform: translateX(-100%);
}
```
<iframe height="265" style="width: 100%;" scrolling="no" title="Vue CSS 轉場 轉場模式 in-out" src="https://codepen.io/CHUPAIWANG/embed/OJPRvVJ?height=265&theme-id=default&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href='https://codepen.io/CHUPAIWANG/pen/OJPRvVJ'>Vue CSS 轉場 轉場模式 in-out</a> by CHUPAIWANG
(<a href='https://codepen.io/CHUPAIWANG'>@CHUPAIWANG</a>) on <a href='https://codepen.io'>CodePen</a>.
</iframe>
## 多個元件的轉場
多個元件的轉場簡單很多,我們不需要使用 `key` 特性。只需要使用 **動態切換** 元件:
```htmlmixed
<!-- .html -->
<div id="app">
<input type="radio" value="v-a" v-model="view" />A
<input type="radio" value="v-b" v-model="view" />B
<transition name="component-fade" mode="out-in">
<component v-bind:is="view"></component>
</transition>
</div>
```
```css
.component-fade-enter,
.component-fade-leave-to {
opacity: 0;
}
.component-fade-enter-active,
.component-fade-leave-active {
transition: opacity 0.3s ease;
}
```
```javascript
const vm = new Vue({
el: '#app',
data: {
view: 'v-a',
},
components: {
'v-a': {
template: '<div>Component A</div>',
},
'v-b': {
template: '<div>Component B</div>',
},
},
});
```