Vue(三)基礎(chǔ),v-if,mixins(混合)等...

混合

組件系統(tǒng)是Vue的核心,如何合理的規(guī)劃組件,是我們在開發(fā)中需要深入思考的問題,我個人習(xí)慣把一些組件使用邏輯和方法,從組件中拆分出來,放到獨(dú)立文件,最后通過混合的方式,引入相關(guān)邏輯,這樣可以在一定層面上把組件再進(jìn)行一次解耦,做到只要切換不同的混合文件就能提供新功能的能力

// 在hello-world項(xiàng)目目錄下,建立basic.vue,在其中輸入下面內(nèi)容
<template>
</template>
<script>
    export default {
        data () {
            return {
                basicInfo: 'basicInfo'
            }
        },
        created () {
            console.log('from basic');
        }
    }
</script>
...
// 在index.vue中引入這個文件,并且進(jìn)行混合
import basic from './basic.vue'; 
...
export default {
        mixins: [basic],
...
// 在index.vue的模板中添加下面內(nèi)容
<p>{{basicInfo}}</p>        

此時(shí)在頁面可以正常渲染出basicInfo的插值,還會打印出basic中的log信息,下面做如下改造:

// 在basic.vue下,添加sayInfo方法
methods: {
    sayInfo () {
        console.log(`from basic.vue,info:${this.basicInfo}`);
    }
}
...
// 在index.vue的模板下,做下面調(diào)整
<p @click="sayInfo">{{basicInfo}}</p>

這時(shí)如果你點(diǎn)擊頁面就會發(fā)現(xiàn)控制臺輸出定義在basic.vue中的信息,可以假設(shè)如果我們切換成另外一個也有sayInfo的組件,這時(shí)點(diǎn)擊頁面就會響應(yīng)另外一個組件中的同名事件,這樣就達(dá)到了切換不同混合邏輯,組件功能也就不同

// 直接調(diào)用被混入對象的值,在basic.vue中是沒有info的值,但是因?yàn)樵趇ndex.vue中有info值,這時(shí)點(diǎn)擊元素一樣可以正常輸出info的值
sayInfo () {
    console.log(`from basic.vue,info:${this.basicInfo}`);
    console.log(this.info);
}

進(jìn)行混合時(shí)有下面幾個規(guī)則需要注意:

  • 當(dāng)組件和引入的混合組件有數(shù)據(jù)沖突時(shí),會以組件的數(shù)據(jù)為準(zhǔn)
// 在basic.vue的文件中,添加info數(shù)據(jù)
data () {
    return {
        basicInfo: 'basicInfo',
        info: 'from basic'
    }
},

此時(shí)按普通組件的邏輯分析,sayInfo應(yīng)該輸出basic.vue的info值,但是因?yàn)榻M件數(shù)據(jù)的優(yōu)先級高于混合組件,當(dāng)出現(xiàn)數(shù)據(jù)沖突時(shí),保留組件的數(shù)據(jù),所以此時(shí)info的值依然還是index.vue中定義的info值,data,methods,computed,directives(值為對象的選項(xiàng))中的同名值都會產(chǎn)生覆蓋

  • 部分鉤子函數(shù)數(shù)據(jù)會產(chǎn)生合并為不是覆蓋
// index.vue的模板做如下改造
<p @click="sayInfo">{{basicInfo}}</p>
<button @click="changeInfo">改變info值</button>
...
// index.vue添加方法
methods: {
    changeInfo () {
        this.info = 'change Info';
    }
}
...
// index.vue添加對info數(shù)據(jù)的監(jiān)聽
watch: {
    info () {
        console.log('watch basic.vue info值發(fā)生改變');
    }
},
...
// index.vue的created鉤子函數(shù)添加如下內(nèi)容
created () {
    console.log('from index.vue created');
},
...
// basic.vue的created鉤子函數(shù)添加如下內(nèi)容
created () {
    console.log('from basic');
},
// basic.vue添加對info監(jiān)聽的watch方法
watch: {
    info () {
        console.log('watch basic.vue info值發(fā)生改變');
    }
},

在控制臺會輸出

from basic
from index.vue created
...
watch basic.vue info值發(fā)生改變
watch index.vue info值發(fā)生了變化

同名鉤子函數(shù)將混合為一個數(shù)組,因此都將被調(diào)用。混入對象的鉤子將在組件自身鉤子之前調(diào)用。

上面介紹的都是在組件內(nèi)的進(jìn)行混合,Vue提供了Vue.mixin用來定義進(jìn)行全局混合,但這種方式太過暴力,會影響所有的組件實(shí)例以及第三方模板,如果有要在全局添加相關(guān)功能的需求可以使用插件來添加。

插件

插件通常會為 Vue 添加全局功能。插件的范圍沒有限制——一般有下面幾種:

  • 添加全局方法或者屬性
  • 添加全局資源:指令/過濾器/過渡等
  • 通過全局 mixin 方法添加一些組件選項(xiàng)
  • 添加 Vue 實(shí)例方法,通過把它們添加到 Vue.prototype 上實(shí)現(xiàn)
  • 一個庫,提供自己的 API,同時(shí)提供上面提到的一個或多個功能

Vue的作者在插件編寫方面提供了很好的封裝,我們可以很容易的編寫出一個Vue插件。

// 在項(xiàng)目下新建一個plugin.js,在其中編寫如下內(nèi)容
let plugin = {};
plugin.install = (Vue, options) => {
    Vue.$msg = 'from plugin';
};
module.exports = plugin;
...
// 在項(xiàng)目的index.js中引入這個文件,并通過Vue.use使用這個插件
import plg from './plugin'; // 引入plugin.js插件

Vue.use(plg);
console.log('Vue' + Vue.$msg);

這時(shí)在控制臺就能輸出,我們剛剛在插件中定義的信息,通過上面的例子,我們可以看出Vue的插件應(yīng)當(dāng)有一個公開方法install。這個方法的第一個參數(shù)是 Vue 構(gòu)造器,第二個參數(shù)是一個可選的選項(xiàng)對象,在方法內(nèi)部可以定義如下幾種信息:

MyPlugin.install = function (Vue, options) {
  // 1\. 添加全局方法或?qū)傩?  Vue.myGlobalMethod = function () {
    // 邏輯...
  }

  // 2\. 添加全局資源
  Vue.directive('my-directive', {
    bind (el, binding, vnode, oldVnode) {
      // 邏輯...
    }
    ...
  })

  // 3\. 注入組件
  Vue.mixin({
    created: function () {
      // 邏輯...
    }
    ...
  })

  // 4\. 添加實(shí)例方法
  Vue.prototype.$myMethod = function (methodOptions) {
    // 邏輯...
  }
}

全局方法或全局屬性就如上例一樣,加掛到Vue上,需要通過訪問Vue來訪問綁定到全局的方法和屬性。在組件內(nèi)部要訪問綁定到Vue的全局變量,需要做如下改造:

// 引入Vue,以及插件
import Vue from 'vue';
import plg from './plugin'; // 引入plugin.js插件
Vue.use(plg);
...
// 這時(shí)在組件內(nèi)部也可以正常訪問綁定在Vue的全局方法或全局屬性
mounted () {
    console.log('Vue' + Vue.$msg);
}

如果要加到實(shí)例上,可以使用Vue.prototype添加

plugin.install = (Vue, options) => {
    Vue.prototype.$msg = 'from plugin';
};
...
// 此時(shí)在在實(shí)例內(nèi)部可以使用this進(jìn)行訪問
mounted () {
    console.log('Vue' + this.$msg);
},

在使用組件時(shí)可以參入?yún)?shù)

Vue.use(plg, {value:'from index.js'});
...
// 在插件中就可以通過options進(jìn)行使用
Vue.prototype.$msg = options.value;

過渡

Vue 在插入、更新或者移除 DOM 時(shí),提供多種不同方式的應(yīng)用過渡效果。

單元素/組件過渡

Vue 提供了 transition 的封裝組件,在下列情形中,可以給任何元素和組件添加 entering/leaving過渡

  • 條件渲染 (使用 v-if)
  • 條件展示 (使用 v-show)
  • 動態(tài)組件
  • 組件根節(jié)點(diǎn)
// 在index.vue的模板頁中添加如下代碼
<button @click="changeShow">過渡效果</button>
<transition>
    <p v-if="showFlag">過渡區(qū)域</p>
</transition>
...
// index.vue的methods下添加下面方法
methods: {
    changeShow () {
        this.showFlag = !this.showFlag;
    }
}

這時(shí)點(diǎn)擊按鈕p標(biāo)簽會在隱藏和顯示中切換,不過這時(shí)沒有過渡的效果,如果要加過渡效果可以添加name屬性。

<transition name="fade">
...
// 在index.vue中添加style標(biāo)簽,在其中添加相關(guān)樣式文件
<style>
    .fade-enter-active, .fade-leave-active {
        transition: opacity 1s;
    }
    .fade-enter, .fade-leave-to {
        opacity: 0;
    }
    .fade-enter-to, .fade-leave {
        opacity: 1;
        color: red;
    }
</style>

當(dāng)插入或刪除包含在 transition 組件中的元素時(shí),Vue 將會做以下處理:

  • 自動嗅探目標(biāo)元素是否應(yīng)用了 CSS 過渡或動畫,如果是,在恰當(dāng)?shù)臅r(shí)機(jī)添加/刪除 CSS 類名。
    在進(jìn)入/離開的過渡中,會有 6 個 class 切換。

    • v-enter:定義進(jìn)入過渡的開始狀態(tài)。在元素被插入時(shí)生效,在下一個幀移除。

    • v-enter-active:定義過渡的狀態(tài)。在元素整個過渡過程中作用,在元素被插入時(shí)生效,在 transition/animation 完成之后移除。這個類可以被用來定義過渡的過程時(shí)間,延遲和曲線函數(shù)。

    • v-enter-to: 2.1.8版及以上 定義進(jìn)入過渡的結(jié)束狀態(tài)。在元素被插入一幀后生效 (與此同時(shí) v-enter 被刪除),在 transition/animation 完成之后移除。

    • v-leave: 定義離開過渡的開始狀態(tài)。在離開過渡被觸發(fā)時(shí)生效,在下一個幀移除。

    • v-leave-active:定義過渡的狀態(tài)。在元素整個過渡過程中作用,在離開過渡被觸發(fā)后立即生效,在 transition/animation 完成之后移除。這個類可以被用來定義過渡的過程時(shí)間,延遲和曲線函數(shù)。

    • v-leave-to: 2.1.8版及以上 定義離開過渡的結(jié)束狀態(tài)。在離開過渡被觸發(fā)一幀后生效 (與此同時(shí) v-leave 被刪除),在 transition/animation 完成之后移除。

class的切換可以簡單理解成,當(dāng)元素剛插入時(shí),會插入v-enter(v-就是transition中的name屬性)和v-enter-active兩個class,然后在下一幀會刪除v-enter,添加上v-enter-to,最后在transition/animation執(zhí)行完成后,會直接刪除v-enter-to和v-enter-active這兩個class,當(dāng)元素離開時(shí)和進(jìn)入時(shí)一致,剛開始離開就添加v-leave和v-leave-active這兩個class,然后在下一幀就刪除v-leave添加v-leave-to。在顯示和離開的整個過程中v-enter-active和v-leave-active是一直存在的,所以才會說在這兩個類中定義過渡時(shí)間,延遲和曲線函數(shù),結(jié)合上面的例子,我們來分析下,整個執(zhí)行過程:

  1. 當(dāng)我們點(diǎn)擊按鈕時(shí),頁面中的p標(biāo)簽進(jìn)入離開狀態(tài),所以Vue會在p標(biāo)簽上創(chuàng)建兩個class,fade-leave、fade-leave-active
  2. 進(jìn)到下一幀,fade-leave會被刪除,fade-leave-to會被添加,所以在點(diǎn)擊標(biāo)簽的時(shí)候才會有字體會猛的變成紅色,然后又變回黑色,然后按照fade-leave-active設(shè)定的動畫,逐漸消失。
  3. 文字消失后,我們再次點(diǎn)擊按鈕,文字會先成紅色,然后逐漸展示出來,最后變成黑色,這也是因?yàn)檎麄€class添加的順序是,先添加fade-enter,fade-enter-active然后再添加fade-enter-to,最后在全部刪除,分析相關(guān)的樣式代碼,我們能很清晰的看出正規(guī)邏輯的執(zhí)行過程。

按官網(wǎng)的說法,由于動畫的不同,Vue針對transition和animation在添加和移除相關(guān)class的時(shí)機(jī)會有一些區(qū)別,這個區(qū)別主要集中在v-enter類名上,下面做如下改造,來驗(yàn)證這個說法:

// 在index.vue的style下,添加下面代碼
.bounce-enter {
    color: red;
    font-size: 28px;
}
.bounce-enter-active {
    animation: bounce-in 2s;
}
.bounce-leave {
    color: red;
}
.bounce-leave-active {
    animation: bounce-in 2s reverse;
}
@keyframes bounce-in {
    0% {
        opacity: 0.5;
    }
    100% {
        opacity: 1;
    }
}
...
// 對模板中的代碼也做相關(guān)調(diào)整,animationstart和animationend是動畫開始和結(jié)束的監(jiān)聽事件
<transition name="bounce">
    <p @animationstart="startEvent" @animationend="endEvent" v-if="showFlag">過渡區(qū)域</p>
</transition>
...
// 添加兩個動畫的監(jiān)聽事件
startEvent (e) {
    console.log('動畫執(zhí)行開始');
    console.log(e.srcElement.className);
    console.log(new Date().getTime());
    console.log(this.showFlag);
},
endEvent (e) {
    console.log('動畫執(zhí)行完畢');
    console.log(e.srcElement.className);
    console.log(new Date().getTime());
    console.log(this.showFlag);
}

通過控制臺的輸出,我們可以很明顯的看出當(dāng)發(fā)生過渡時(shí),class的變化,當(dāng)元素移除時(shí),class變化是先為:bounce-leave bounce-leave-active,然后再變?yōu)椋篵ounce-leave-active bounce-leave-to,元素添加時(shí),class的變化是先為:bounce-enter bounce-enter-active,然后再變?yōu)閎ounce-enter-active bounce-enter-to,這個值的變化也驗(yàn)證了,我們之前所說的執(zhí)行邏輯。如果你仔細(xì)觀察頁面中元素的變化也能看出對應(yīng)的樣式也是生效的,不過這里有問題,當(dāng)你多點(diǎn)擊幾次,會發(fā)現(xiàn)好像v-enter對應(yīng)的樣式并不是每一次都生效,有時(shí)會感覺Vue在執(zhí)行時(shí)會忽略v-enter/v-leave的樣式,這個問題暫時(shí)還沒想明白,不知道是不是我設(shè)置的樣式的問題?

另外要注意的是必須在v-enter-active、v-leave-active或v-enter-to、v-leave-to中設(shè)置transition或animation動畫,要不整個過渡效果看起來像是沒有執(zhí)行一樣,頁面很快的發(fā)生變化,可以做如下的驗(yàn)證:

// 隱藏transition
.fade-enter-active, .fade-leave-active {
    /*transition: opacity 2s;*/
    color: #00aceb;
    font-size: 30px;
}

此時(shí)頁面如預(yù)期一樣,發(fā)生了很快的變化,完全看不出有什么過渡效果

// 這時(shí)雖然可以這樣改造,讓過渡繼續(xù)生效,不過從整個執(zhí)行過程上來看,顯然沒有放到active上好
.fade-enter-to, .fade-leave-to {
    transition: opacity 2s;
}

除了可以使用transition對應(yīng)的name自動生成的class,還可以指定某個class:

enter-class                 // 替換v-enter
enter-active-class          // 替換v-enter-active
enter-to-class              // 替換v-enter-to
leave-class                 // 替換v-leave
leave-active-class          // 替換v-leave-active
leave-to-class              // 替換v-leave-to

可以做如下驗(yàn)證:

<transition name="fade" enter-active-class="intoClass" enter-to-class="externalClass">
...
.externalClass {
    color: #faf2cc;
}
.intoClass {
    transition: opacity 2s;
}

這時(shí)你切換頁面會發(fā)現(xiàn)當(dāng)元素重新插入時(shí),綁定的class就變成了intoClass和externalClass,這種規(guī)則,對引入外部動畫庫有比較友好的支持,只需要把需要執(zhí)行的相關(guān)class進(jìn)行替換。

使用type設(shè)定Vue監(jiān)聽動畫的類型

過渡效果如果只是transition或者只是animation,Vue可以很明顯的知道要監(jiān)聽什么,但當(dāng)過渡和動畫同時(shí)存在時(shí),如果不指定監(jiān)聽類型,Vue就不能明確的知道監(jiān)聽哪個。

// 樣式文件做相關(guān)修改,過渡和動畫同時(shí)存在
.fade-enter-active, .fade-leave-active {
    transition: opacity 5s;
    animation: bounce-in 2s reverse;
}
@keyframes bounce-in {
    0% {
        transform: scale(0);
    }
    50% {
        transform: scale(1.5);
    }
    100% {
        transform: scale(1);
    }
}

這時(shí)點(diǎn)擊頁面如果移除元素,會發(fā)現(xiàn)class在transition執(zhí)行完成才會移除,如果此時(shí)是以transition為主,是沒有問題的,但如果想以animation為主,就要設(shè)定type值

// type值可以為animation,transition
<transition name="fade" type="animation">

這時(shí)再點(diǎn)擊頁面會發(fā)現(xiàn)此時(shí)過渡效果是以animation的動畫為準(zhǔn),如果你希望動畫要持續(xù)足夠長的時(shí)間,可以保證所有動畫過渡都執(zhí)行完,可以使用duration屬性,設(shè)定過渡持續(xù)時(shí)間。

// duration單位是毫秒,下面這樣設(shè)定表示,過渡持續(xù)8秒(進(jìn)入和離開都是8秒)
<transition name="fade" type="animation" :duration="8000">
...
// 還可以設(shè)定的更詳細(xì),{enter: 2000, leave: 8000}進(jìn)入時(shí)過渡時(shí)間持續(xù)2秒,離開時(shí)持續(xù)8秒
<transition name="fade" type="animation" :duration="{enter: 2000, leave: 8000 }">

  • 如果過渡組件提供了 JavaScript 鉤子函數(shù),這些鉤子函數(shù)將在恰當(dāng)?shù)臅r(shí)機(jī)被調(diào)用。

除了普通的css動畫庫還有以js為主的動畫庫,Vue提供這方面的能力,估計(jì)很大一方面就是讓Vue能和這些js庫結(jié)合,使用js鉤子函數(shù)會使動畫操作更為準(zhǔn)確,能比較精確的控制在某些狀態(tài)下頁面行為,這是css動畫不好實(shí)現(xiàn)的地方,官方文檔在這部分使用的是Velocity.js這個動畫庫(隱隱的神傷。。想寫原生的動畫,怎么實(shí)現(xiàn)都有問題,標(biāo)記下回頭找找原因)

// 在項(xiàng)目最外層目錄運(yùn)行下面命令,安裝velocity-animate動畫庫
npm install velocity-animate --save
...
// 在index.vue中引入這個庫
import Velocity from 'velocity-animate'; // 引入velocity-animate動畫庫
...
// index.vue模板文件做如下修改
<transition @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter" @enter-cancelled="enterCancelled" @before-leave="beforeLeave" @leave="leave" @after-leave="afterLeave" @leave-cancelled="leaveCancelled" :css="false">
...
// index.vue添加相關(guān)方法
beforeEnter (el) {
    console.log('beforeEnter');
    el.style.opacity = 0;
    el.style.transformOrigin = 'left';
},
enter (el, done) {
    console.log('enter');
    Velocity(el, { opacity: 1, fontSize: '1.4em' }, { duration: 300 });
    Velocity(el, { fontSize: '1em' }, { complete: done });
},
afterEnter (el) {
    console.log('afterEnter');
},
enterCancelled (el) {
    console.log('enterCancelled');
},
beforeLeave (el) {
    console.log('beforeLeave');
},
leave (el, done) {
    console.log('leave');
    Velocity(el, { translateX: '15px', rotateZ: '50deg' }, { duration: 600 });
    Velocity(el, { rotateZ: '100deg' }, { loop: 2 });
    Velocity(el, {
        rotateZ: '45deg',
        translateY: '30px',
        translateX: '30px',
        opacity: 0
    }, { complete: done })
},
afterLeave (el) {
    console.log('afterLeave');
},
leaveCancelled (el) {
    console.log('leaveCancelled');
}

點(diǎn)擊按鈕,可以看到控制臺依次輸出

beforeEnter
enter
afterEnter(動畫結(jié)束后輸出)
...
beforeLeave
leave
afterLeave(動畫結(jié)束后輸出)

在進(jìn)入或移除動畫沒有運(yùn)行完成時(shí),點(diǎn)擊按鈕,中斷動畫這時(shí)就會輸出

enterCancelled/leaveCancelled

官方文檔上說,在 enter 和 leave 中,回調(diào)函數(shù) done 是必須的 。否則,它們會被同步調(diào)用,過渡會立即完成。注意leave (el, done)和enter (el, done)多接收了一個參數(shù),并在{ complete: done }中進(jìn)行了觸發(fā),沒特別看出過渡立即完成這個效果,可能是和使用了Velocity動畫庫有關(guān),不過如果不添加這個done的回調(diào),after-enter的鉤子函數(shù)是不能正常調(diào)用,并且在下一次動畫中會先執(zhí)行enter-cancelled的鉤子函數(shù)。

:css="false"是忽略對過渡元素添加class(官方文檔上說時(shí)不對過渡元素進(jìn)行css檢查,沒特別看出來,不過不加這個屬性,過渡元素會添加v-enter,v-enter-active等class,添加了就不會添加)

使用appear設(shè)定元素初始化時(shí)的過渡/動畫

通過設(shè)定appear屬性可以讓組件開始渲染時(shí)一樣有過渡效果

// 添加appear屬性是表示啟用此特性,appear-class,appear-to-class,appear-active-class和v-enter,v-enter-to,v-enter-active存在周期一致
<transition appear appear-class="custom-appear-class" appear-to-class="custom-appear-to-class" appear-active-class="custom-appear-active-class">
    <p v-if="showFlag">過渡區(qū)域</p>
</transition>
...
// 樣式文件中添加如下內(nèi)容
.custom-appear-class{
    font-size: 40px;
    color: red;
    opacity: 0;
}
.custom-appear-to-class {
    font-size: 18px;
}
.custom-appear-active-class{
    opacity: 1;
    transition: all 5s;
}
...
// 這時(shí)要把showFlag改為true,才能看出這個效果,因?yàn)檫@個加載時(shí)的過渡效果只會在進(jìn)入時(shí)啟用,渲染完成后就不會再使用
showFlag: true,

這時(shí)刷新頁面就會發(fā)現(xiàn)當(dāng)前元素在第一次渲染時(shí)會有一個進(jìn)入過渡效果,再進(jìn)行相關(guān)操作,這個過渡效果也不會出來

多元素過渡

在某些情況下會碰到在一個區(qū)域存在因?yàn)椴煌瑮l件展示不同元素的情況,比如

// 此時(shí)這個key值的其實(shí)并不是很必要,因?yàn)閎utton并不會像input元素那樣存在用戶輸入的緩存信息,但Vue官方建議在這種情況下加上key讓元素強(qiáng)制重新渲染,讓過渡動畫可以正常觸發(fā),如果此時(shí)不加key值,因?yàn)槲覀兏膭拥闹皇俏陌?,Vue會盡可能復(fù)用代碼,并不會重新渲染當(dāng)前元素,也就不會出現(xiàn)過渡效果
<button @click="changeState">改變文字</button>
<transition name="fade">
    <button :key="docState">
        {{ buttonMessage }}
    </button>
</transition>
...
// data下添加docState
docState: 'saved',
...
// methods下添加changeState
changeState () {
    switch (this.docState) {
        case 'saved':
            this.docState = 'edited';
            break;
        case 'edited':
            this.docState = 'editing';
            break;
        case 'editing':
            this.docState = 'saved';
            break;
    }
},

此時(shí)點(diǎn)擊頁面我們會看到一個比較丑陋的按鈕切換效果,一個按鈕正在消失,另一個按鈕同時(shí)出現(xiàn),此時(shí)我們可以借助Vue提供的過渡模式來使進(jìn)出過渡切換的更為自然

// mode: out-in(當(dāng)前元素先進(jìn)行過渡,完成之后新元素過渡進(jìn)入); in-out(新元素先進(jìn)行過渡,完成之后當(dāng)前元素過渡離開)
<transition name="fade" mode="out-in">

這時(shí)頁面的過渡效果就比較自然了,一個元素消失,另外一個元素漸隱出現(xiàn),如果是多個組件的過渡可以使用動態(tài)組件來實(shí)現(xiàn)

// 在index.vue的模板中做如下調(diào)整
<button @click="changeTab">改變tab</button>
<transition name="fade" mode="out-in">
    <component :is="showTabView"></component>
</transition>
...
// 添加相關(guān)方法
changeTab () {
    switch (this.showTabView) {
        case 'gameList':
            this.showTabView = 'newsList';
            break;
        case 'newsList':
            this.showTabView = 'sportList';
            break;
        case 'sportList':
            this.showTabView = 'gameList';
            break;
    }
}

這時(shí)在頁面端就添加了對動態(tài)組件的過渡效果,上面我們看了單個元素的顯示隱藏觸發(fā)的過渡效果,和在同一區(qū)域不同元素和組件切換時(shí)的過渡效果,除此之外,還有列表的過渡效果沒有講,在官方文檔,直接告訴我們對列表使用transition-group,但為什么不可以使用transition呢,我們使用一個實(shí)例來驗(yàn)證下:

// index.vue的模板改造
<button @click="add">Add</button>
<button @click="remove">Remove</button>
<ul class="ulList">
<transition name="fade">
    <li v-for="item in numList">{{item}}</li>
</transition>
</ul>
...
// data添加下面屬性
numList: [1, 2, 3, 4, 5, 6, 7, 8, 9],
nextNum: 10,
...
// 添加下面樣式
.ulList li {
    list-style: none;
}
...
// 添加相關(guān)方法
randomIndex: function () {
    return Math.floor(Math.random() * this.numList.length)
},
add: function () {
    this.numList.splice(this.randomIndex(), 0, this.nextNum++)
},
remove: function () {
    this.numList.splice(this.randomIndex(), 1)
}

這時(shí)頁面會直接提示transition只能用在單個元素,如果要使用在列表元素需要使用transition-group。

// 換成transition-group,但此時(shí)子元素li沒有添加key值,一樣報(bào)錯提示transition-group的子元素必須包含key值
<transition-group name="fade">
    <li v-for="item in numList">{{item}}</li>
...
// 添加key值
    <li v-for="item in numList" :key="item">{{item}}</li>

此時(shí)頁面渲染為

<button>Add</button> 
<button>Remove</button> 
<ul class="ulList">
    <span>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
        <li>6</li>
        <li>7</li>
        <li>8</li>
        <li>9</li>
    </span>
</ul>
...
// ul下的span是transition-group默認(rèn)進(jìn)行添加的,如果你想改動可以使用tag屬性
// 沒看到官方文檔上說如何在包裹外層元素時(shí),同時(shí)添加元素的class,這個問題感覺也沒那么重要,在外面包裹一個div直接添加class就行了
<button @click="add">Add</button>
<button @click="remove">Remove</button>
<div class="ulList">
    <transition-group name="list" tag="ul">
        <li v-for="item in numList" :key="item">{{item}}</li>
    </transition-group>
</div>

此時(shí)頁面如果再添加元素,就會有過渡效果,不過此時(shí)過渡雖然有了,但是有些僵硬,一個元素插入,其他元素直接就跑到該到的位置,沒有流暢感,這里Vue使用了FLIP動畫(后面說動畫時(shí)會詳細(xì)講解)來解決這個問題

// 在樣式文件中添加如下內(nèi)容
.list-move {
    transition: transform 1s;
}

此時(shí)頁面的渲染就會在元素插入時(shí),其他元素一起過渡起來,看起來就變的很流暢,除了自動添加的樣式(因?yàn)獒槍^渡元素設(shè)定的是list過渡,這時(shí)的list-move是Vue自動判斷的,如果不需要這個自動添加的,可以使用:move-class設(shè)定不同的class)

  • 如果沒有找到 JavaScript 鉤子并且也沒有檢測到 CSS 過渡/動畫,DOM 操作 (插入/刪除) 在下一幀中立即執(zhí)行。

與第三方庫的結(jié)合達(dá)到數(shù)據(jù)元素本身的過渡

上面介紹的都是以元素為基本條件觸發(fā)的過渡,如果只是元素本身數(shù)據(jù)的更新,這時(shí)要進(jìn)行過渡就要引入第三方庫來實(shí)現(xiàn)這些過渡,這些包括且不限于:

  • 數(shù)字和運(yùn)算
  • 顏色的顯示
  • SVG 節(jié)點(diǎn)的位置
  • 元素的大小和其他的屬性

這部分的內(nèi)容我理解和動畫掛鉤的比較多,所以這些內(nèi)容會在動畫這部分講解。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容