這篇文章是接著我的上部vue學習筆記寫的,之所以分開寫這兩篇文章是因為我意識到不知道什么時候就寫了非常多的字了,所以我決定拆分來寫,這一篇我一定寫得比上一篇還要簡潔爭取用更少的字更通俗易懂的解釋讓大家都能學會甚至精通vue
過度&動畫
Vue 提供了 transition 的封裝組件用于封裝需要過渡的組件
Vue過渡的類名
v-enter:定義進入過渡的開始狀態(tài)。在元素被插入之前生效晦雨,在元素被插入之后的下一幀移除。
v-enter-active:定義進入過渡生效時的狀態(tài)南片。在整個進入過渡的階段中應用,在元素被插入之前生效病涨,在過渡/動畫完成之后移除粟誓。這個類可以被用來定義進入過渡的過程時間枷邪,延遲和曲線函數(shù)。
v-enter-to: 2.1.8版及以上 定義進入過渡的結束狀態(tài)抖部。在元素被插入之后下一幀生效 (與此同時 v-enter 被移除)说贝,在過渡/動畫完成之后移除。
v-leave: 定義離開過渡的開始狀態(tài)慎颗。在離開過渡被觸發(fā)時立刻生效乡恕,下一幀被移除。
v-leave-active:定義離開過渡生效時的狀態(tài)俯萎。在整個離開過渡的階段中應用傲宜,在離開過渡被觸發(fā)時立刻生效,在過渡/動畫完成之后移除夫啊。這個類可以被用來定義離開過渡的過程時間函卒,延遲和曲線函數(shù)。
v-leave-to: 2.1.8版及以上 定義離開過渡的結束狀態(tài)撇眯。在離開過渡被觸發(fā)之后下一幀生效 (與此同時 v-leave 被刪除)报嵌,在過渡/動畫完成之后移除。
這樣就會實現(xiàn)一個非常簡單的小小動畫,給大家詳細解釋一下
v-enter 動畫開始之前的狀態(tài) 這里設置 opacity 0 , v-enter-to 動畫的結束狀態(tài),但是會從v-enter被移除一直到動畫結束才會被移除,然后結合v-enter-active設置的transition就可以完美的實現(xiàn)動畫,可能大家還有疑惑,那么我這樣設置動畫你懂了吧
<style>
/* 這兩個代表顯示,文字慢慢顯示出來 */
.v-enter {
opacity: 0;
}
.v-enter-to {
opacity: 1;
}
.v-enter-active {
/* 結合此類設置的transition,vue動畫就實現(xiàn)了,懂了嗎 */
transition: all 300s ease 0s;
}
/* 這兩個代表離開自然起始狀態(tài)是1,結束透明度為0唄 */
.v-leave {
opacity: 1;
}
.v-leave-to {
opacity: 0;
}
.v-leave-active {
/* 我決定裝個怪 */
transition: all .3s ease 0s;
}
</style>
Vue提供的自定義標簽transition提供了一個name屬性,用來替換vue動畫中的css類名中的v
<div id="app">
<button type="button" @click="show = !show">切換顯示隱藏</button>
<!-- transition提供一個name屬性,里面的值可以替換css動畫類名的v -->
<transition name="bounce">
<p v-show="show">這是一個需要被過渡的元素</p>
</transition>
</div>
都是有點基礎的,所以我這下只貼核心代碼了
可以使用transition的一些屬性定義對應的類名
- enter-class
- enter-active-class
- enter-to-class (2.1.8+)
- leave-class
- leave-active-class
- leave-to-class (2.1.8+)
每一個屬性代表一個vue動畫類名,這對于我們使用第三方動畫庫非常有用,這里我們可以將vue官方的例子copy下來直接用
<link rel="stylesheet" type="text/css">
Vue的transition中提供了一個duration屬性,用于指定動畫的完成時間,如果不設置vue可以默認計算出對應的動畫的完成時間,但是當我們顯示指定動畫的完成時間之后,vue就會在對應的指定的時間內完成動畫,算了說不清看例子
<body>
<div id="app">
<button @click="show = !show">
Toggle render
</button> <br>
<transition :duration="500">
<p v-if="show">這是一個測試的文字</p>
</transition>
</div>
<style>
p {
position: absolute;
left: 50px;
top: 50px;
}
.v-enter,
.v-leave-to {
opacity: 0;
left: 500px;
transform: scale(2);
}
.v-enter-to,
.v-leave {
opacity: 1;
transform: scale(1);
left: 50px;
}
.v-enter-active,.v-leave-active{
transition: all 3s;
}
</style>
上面這個例子中我故意將duration設置為500(ms),然后過渡的時間為3s,這就代表著當你開始動畫時vue會以你設置的500ms為動畫時間所以實際上當動畫了500ms之后vue就會執(zhí)行動畫之后的操作也就是
opacity 0 left500px scale2 ,大致上就是動畫到你設置的duration時之后就會直接干嘣到對應的樣式中,大家直接試一下就行了
同時duration可以指定更細致的進入和離開動畫的時間
<transition :duration="{enter:1000,leave:3000}">
<p v-if="show">這是一個測試的文字</p>
</transition>
這樣設置你會發(fā)現(xiàn)元素就類似于進入動畫為1s會干嘣,但是離開動畫正好3s不會干嘣(最后小伙伴們肯定想問,那么我的duration設置大于transition的過渡時長會怎么辦呢?那肯定是動畫全程過渡完成,然后再等一段時間才能觸發(fā)另外一種動畫唄,因為vue會以你設置的duration為基準進行判斷動畫的執(zhí)行情況)你實在不懂,直接設置一次,動畫的時候看動畫的類名就行了
動畫鉤子函數(shù)
vue在動畫從開始到結束的這一過程中,提供了許多鉤子函數(shù)方便我們更好的實現(xiàn)我們的邏輯,具體如下
- before-enter
- enter
- after-enter
- enter-cancelled --> 說說這個吧,就是當你正在進行開始動畫的時候,觸發(fā)了某種條件需要執(zhí)行結束動畫,這時候這個鉤子函數(shù)生效)
- before-leave
- leave
- after-leave
- leave-cancelled -->(此鉤子函數(shù)在v-if中無效,只能用于v-show)
直接小小演示一遍就行了,這里需要使用v-on監(jiān)聽這些事件,在transition組件中監(jiān)聽
<body>
<div id="app">
<button @click="show = !show">
Toggle render
</button> <br>
<transition @before-enter="beforeEnter" @enter="Enter" @after-enter="afterEnter"
@enter-cancelled="enterCancelled" @before-leave="beforeLeave" @leave="Leave" @after-leave="afterLeave"
@leave-cancelled="leaveCancelled">
<p v-show="show">這是一個測試的文字</p>
</transition>
</div>
<style>
p {
position: absolute;
left: 50px;
top: 50px;
}
.v-enter,
.v-leave-to {
opacity: 0;
left: 500px;
transform: scale(2);
}
.v-enter-to,
.v-leave {
opacity: 1;
transform: scale(1);
left: 50px;
}
.v-enter-active,
.v-leave-active {
transition: all .3s linear 0s;
}
</style>
<script src="../lib/vue.js"></script>
<script>
new Vue({
data: {
show: true
},
methods: {
// 動畫準備中,每個函數(shù)都有一個el參數(shù),用來指示正在動畫的元素
beforeEnter() {
console.log('beforeEnter');
},
// 動畫準備完成
Enter(el,done) {
console.log('Enter');
setTimeout(() => {
done();
}, 1000);
},
afterEnter() {
console.log('afterEnter');
},
enterCancelled() {
console.log('enterCancelled');
},
beforeLeave() {
console.log('beforeLeave');
},
Leave(el,done) {
console.log('Leave');
setTimeout(() => {
done();
}, 1000);
},
afterLeave() {
console.log('afterLeave');
},
leaveCancelled() {
console.log('leaveCancelled');
}
},
}).$mount("#app");
</script>
</body>
(每一個鉤子函數(shù)都有一個可選的el,代表正在動畫的元素)
這里其實一切按道理來說都是沒問題的,Vue為Enter和Leave兩個鉤子函數(shù)提供了一個可選的done()回調,這個回調有什么用呢?
這是一個賊坑的東西,一個正常的動畫流程是 beforeEnter-->Enter-->afterEnter,因為Enter有一個done回調,代表著如果你不調用done()回調那么動畫就不知道什么時候完成,所以在你不調用done的回調的時候你會發(fā)現(xiàn),鉤子函數(shù)afterEnter不會調用,當你切換動畫的時候就是enterCancelled函數(shù)調用了,這就是因為Vue只有在你調用了done函數(shù)之后才認為動畫已經執(zhí)行結束了,然后當你直接調用done()函數(shù)就會出現(xiàn)干嘣的情況,為什么呢?(很坑啊)就是因為Enter()函數(shù)是在動畫準備完成的時候調用,這不就代表著動畫一準備完成就done(動畫不就完成了嗎?)所以就干嘣了,那就只能苦逼的寫setTimeOut了嗎?不是的,在純CSS3動畫的時候我們只需要不接收這個done函數(shù)一切就正常了,像這樣
一旦選擇接受done函數(shù),就必須承擔就收done函數(shù)需要做的事情,標記動畫什么時候結束,這主要是用于js實現(xiàn)動畫的,看看下面的小例子
new Vue({
el: '#example-4',
data: {
show: false
},
methods: {
beforeEnter: function (el) {
el.style.opacity = 0
el.style.transformOrigin = 'left'
},
enter: function (el, done) {
Velocity(el, { opacity: 1, fontSize: '1.4em' }, { duration: 300 })
Velocity(el, { fontSize: '1em' }, { complete: done })
},
leave: function (el, done) {
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 })
}
}
})
大致上就是這樣用的
初始渲染過渡
Vue還提供了一個叫做元素的初始渲染,就是元素的進場狀態(tài),只會渲染一次,在第一次渲染時
<transition
appear
appear-class="custom-appear"
appear-to-class="custom-appear-to"
appear-active-class="custom-appear-active"
>
p {
position: absolute;
left: 50px;
top: 50px;
}
.custom-appear{
top: 500px;
left: 300px;
}
.custom-appear-to{
left: 50px;
top: 50px;
}
.custom-appear-active{
transition: all 1s linear 0s;
}
這里的四個屬性一個都不能少,第一個appear也不能少,這樣就形成了一個元素的初始進場動畫了,同樣的這個appear也是有鉤子函數(shù)的
- before-appear
- appear
- after-appear
- appear-cancelled
就不顯示了都是一樣的,appear鉤子函數(shù)一樣有可選的done回調(實測)
多個元素的過渡
有時我們可能需要多個元素之間的過渡,對于多個元素之間的過渡,vue官方有一句話
當有相同標簽名的元素切換時熊榛,需要通過 key 特性設置唯一的值來標記以讓 Vue 區(qū)分它們锚国,否則 Vue 為了效率只會替換相同標簽內部的內容。即使在技術上沒有必要玄坦,給在 <transition> 組件中的多個元素設置 key 是一個更好的實踐血筑。
大致上意思就是,兩個相同標簽元素進行過渡,必須給每一個標簽指定一個唯一的key,不然的話vue會直接替換標簽中的內容,不會進行動畫
我這里有兩個h1
如果不加唯一的key的話,就是干嘣狀態(tài),這里說一下transition組件中只能有一個唯一的根元素,我這里兩個h1是因為最后只會渲染一個元素出來,渲染兩個直接報錯了,我們必須使用key指定每個標簽,以區(qū)分他們,這樣才能實現(xiàn)過渡,當然我們可以進行上面這樣的寫法簡寫
<div id="app">
<button @click="show=!show">toggle show</button>
<transition name="m" mode="out-in">
<h1 :key="show">
<!-- 當key發(fā)生改變,即代表著元素發(fā)生改變,然后對應的計算屬性發(fā)生改變 -->
{{mytext}}
</h1>
</transition>
computed: {
mytext() {
if(this.show){
return "我是第一個";
}else{
return "我是第二個";
}
}
},
大家都是有經驗的了,就只貼核心代碼了(這個我實在解釋不清楚了,大家盡量看吧),vue是以key的不同來區(qū)分同標簽元素的,那么上面的例子,我們的key發(fā)生了變化,就需要執(zhí)行類似于轉換標簽那種(key不同標簽不同)就需要銷毀當前key的標簽(銷毀就是離場動畫嘛)然后重新計算新的標簽插入就是進場動畫了,在不懂沒轍了
過渡模式
transition默認執(zhí)行動畫是enter和leave是同步執(zhí)行的,但是transition提供了一個mode選項可以讓我們選擇排隊執(zhí)行動畫,這是一個同步執(zhí)行的例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue組件介紹</title>
</head>
<body>
<div id="app">
<transition>
<button :key="doSomet" @click="doSomet=!doSomet">
{{ msgShow }}
</button>
</transition>
</div>
<style>
button{
position: absolute;
left: 50px;
top: 50px;
}
.v-enter{
transform: translateX(100%);
opacity: 0;
}
.v-enter-to{
transform: translateX(0);
opacity: 1;
}
.v-leave-to{
transform: translateX(-100%);
opacity: 0;
}
.v-enter-active,.v-leave-active{
transition: all 1s linear 0s;
}
</style>
<script src="../lib/vue.js"></script>
<script>
new Vue({
data: {
doSomet:false
},
computed: {
msgShow() {
if(this.doSomet){
return "打開";
}else{
return "關閉";
}
}
},
methods: {
}
}).$mount("#app");
</script>
</body>
</html>
如果我們需要排隊執(zhí)行動畫的話,transition有一個mode屬性實現(xiàn)了這個功能,此屬性一共有兩個選項
- in-out 先進后出
- out-in 先出后進
這兩個相比默認的同步執(zhí)行的差別還是蠻大的,都是排隊執(zhí)行動畫,一個動畫結束才執(zhí)行后一個
多個組件間的過渡
多個組件之間的過渡我們只需要使用動態(tài)組件就行了,因為動態(tài)組件渲染出來就是一個唯一的組件
<transition name="component-fade" mode="out-in">
<component v-bind:is="view"></component>
</transition>
<body>
<div id="app">
<button @click="view=='ani_a'?view='ani_b':view='ani_a'">toggle show</button>
<transition name="com-fade" mode="out-in">
<component :is="view"></component>
</transition>
</div>
<style>
.com-fade-enter,.com-fade-leave-to{
opacity: 0;
}
.com-fade-enter-to,.com-fade-leave{
opacity: 1;
}
.com-fade-enter-active,.com-fade-leave-active{
transition: all 1s linear;
}
</style>
<script src="../lib/vue.js"></script>
<script>
new Vue({
data: {
view:'ani_a'
},
components:{
'ani_a':{
template:"<h1>我是動畫a組件</h1>"
},
'ani_b':{
template:"<h1>我是動畫b組件</h1>"
}
}
}).$mount("#app");
</script>
列表過渡
接下來介紹一下怎樣同事渲染整個列表,vue有提供一個新的組件叫做
transition-group
,這個組件有幾個特點
- 不同于
<transition>
,它會以一個真實元素呈現(xiàn):默認為一個<span>
煎楣。你也可以通過tag
特性更換為其他元素豺总。 - 過渡模式不可用,因為我們不再相互切換特有的元素择懂。
- 內部元素 總是需要 提供唯一的
key
屬性值喻喳。
下面是一個官方的一個transition-group簡單使用
<body>
<div id="list-demo" class="demo">
<button v-on:click="add">Add</button>
<button v-on:click="remove">Remove</button>
<transition-group name="list" tag="p">
<span v-for="item in items" v-bind:key="item" class="list-item">
{{ item }}
</span>
</transition-group>
</div>
<style>
.list-item {
display: inline-block;
margin-right: 10px;
}
.list-enter-active,
.list-leave-active {
transition: all 1s;
}
.list-enter,
.list-leave-to
{
opacity: 0;
transform: translateY(30px);
}
</style>
<script src="../lib/vue.js"></script>
<script>
new Vue({
el: '#list-demo',
data: {
items: [1, 2, 3, 4, 5, 6, 7, 8, 9],
nextNum: 10
},
methods: {
randomIndex: function () {
return Math.floor(Math.random() * this.items.length)
},
add: function () {
this.items.splice(this.randomIndex(), 0, this.nextNum++)
},
remove: function () {
this.items.splice(this.randomIndex(), 1)
},
}
})
</script>
</body>
列表的排序過渡
vue有一個v-move特性,它會在元素的改變定位的過程中應用。這個屬性跟vue的一些動畫類名差不多,當
transition-group
加上name時也會改變其前綴,大家可以看看下面的例子
<body>
<div id="app">
<button type="button" @click="shuffle">shuffle</button>
<transition-group tag="ul">
<li v-for="item of items" :key="item">
{{item}}
</li>
</transition-group>
</div>
<style>
.v-move{
transition: all 1s;
}
</style>
<script src="../lib/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.1/lodash.min.js"></script>
<script>
new Vue({
data() {
return {
items:[1,2,3,4,5,6,7,8,9]
}
},
methods: {
shuffle(){
this.items = _.shuffle(this.items);
}
},
}).$mount("#app");
</script>
</body>
接下來我們可以將v-move屬性結合之前的小例子讓插入數(shù)值時,列表的選項可以平滑的改變而不是干嘣,我們完全只需要在之前的例子中添加一個樣式就行了
.list-move{
transition: all .3s;
}
這樣我們就實現(xiàn)了插入一個數(shù)字時其他的條目是平滑移動的,然后我們繼續(xù)在這基礎上融合兩個例子,只需要在html中添加一個按鈕
<button type="button" @click="shuffle">shuffle</button>
在methods中添加對應的方法就行了,
shuffle(){
this.items = _.shuffle(this.items);
}
//記得添加對應的lodash庫
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.1/lodash.min.js"></script>
兩個例子就這樣融合了,大家可以改變一個transition-group
渲染的p標簽樣式
p{
display: block;
width: 400px;
}
然后嘗試一下,接下來我們繼續(xù)體驗這個v-move屬性
>需要注意的是使用 v-move 過渡的元素不能設置為 display: inline 休蟹。作為替代方案沸枯,可以設置為 display: inline-block 或者放置于 flex 中
同樣的我們也可以使用v-move特性實現(xiàn)一個類似于表格的打亂平滑過渡,下面的例子最好大家看看注釋,有一個超級大坑在(反正我是花了近半天的時間邁過,看不看自己決定)
<body>
<div id="app">
<button @click="shuffle">動起來</button>
<!--
剛開始做這個例子的時候,transition-group使用了tag為table標簽
然后是在tr,td中vFor的(那時用的二維數(shù)組),結果就是無法訪問
vm的數(shù)據,后來測試其他標簽都是正常的(div,p進行訪問),然后
推測tr,td之類的標簽只能用在table標簽中使用,可能是因為vue先解析的是
標簽之中的一些計算數(shù)據吧,然后發(fā)現(xiàn)不在table標簽中包著就失效了
大家看我下面的例子,我使用td包裹內容,但是頁面卻并沒有打印td標簽
或者我直接在html中寫一個td,實時證明,tr,td必須在table下否則無法顯示的
-->
<table>
<tr>
<td></td>
</tr>
</table>
<!-- 不會顯示 -->
<td></td>
<tr></tr>
<!-- ul li為什么行因為,不用ul,li也可以單獨存在啊 -->
<li></li>
<!--
所以使用transition-group切記切記這個超級無敵大坑,
某些必須依賴父親的標簽千萬不要寫在這里面,不然你就算transition-group
tag改成必須依賴的父元素也是不行的,切記切記
昨天就是因為這個超級大坑,導致到現(xiàn)在才明白,繼續(xù)更新這個動畫
-->
<transition-group tag="div">
<span v-for="val in items" :key="val.id">
<td>{{val.number}}</td>
</span>
</transition-group>
</div>
<style>
.v-move {
transition: all 1s ease-in-out;
}
div{
width: 288px;
}
span {
display: inline-block;
width: 30px;
height: 30px;
font-size: 15px;
line-height: 30px;
text-align: center;
border: 1px solid #ccc;
border-collapse: collapse;
user-select: none;
}
</style>
<script src="../lib/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.1/lodash.min.js"></script>
<script>
new Vue({
data() {
return {
items: Array.apply(null, { length: 81 })
.map(function (_, index) {
return {
id: index,
number: index % 9 + 1
}
})
}
},
methods: {
shuffle() {
this.items = _.shuffle(this.items);
}
},
}).$mount("#app");
</script>
</body>
列表的交錯過渡
什么叫做交錯過渡呢,就是比如說一個列表可以錯開時間進行動畫類似于這樣的效果,有時候你會發(fā)現(xiàn)錯開時間的動畫比不錯開時間的動畫更加棒
<body>
<div id="app">
<input type="text" v-model="query">
<transition-group tag="ul" v-bind:css="false" @before-enter="beforeEnter" @enter="enter" @leave="leave">
<li v-for="(item ,index) in computedList" :key="item.msg" :data-index="index">
{{item.msg}}
</li>
</transition-group>
</div>
<style>
</style>
<script src="../lib/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>
<script>
new Vue({
data() {
return {
query: '',
list: [
{ msg: "大鋼炮" },
{ msg: "明明" },
{ msg: "紅紅" },
{ msg: "張三" },
{ msg: "李四" },
{ msg: "明紅" }
]
}
},
computed: {
computedList() {
let vm = this;
return this.list.filter((item) => {
return item.msg.indexOf(vm.query) !== -1;
});
}
},
methods: {
beforeEnter(el) {
el.style.opacity = 0;
el.style.height = 0;
},
enter(el, done) {
// 計算延遲,交錯執(zhí)行動畫
var delay = el.dataset.index * 150
setTimeout(function () {
Velocity(
el,
{ opacity: 1, height: '1.6em' },
{ complete: done }
)
}, delay)
},
leave(el, done) {
var delay = el.dataset.index * 150
setTimeout(function () {
Velocity(
el,
{ opacity: 0, height: 0 },
{ complete: done }
)
}, delay)
}
},
}).$mount("#app");
</script>
</body>
大家直接看這個動畫交錯執(zhí)行的小例子吧,這里通過dataset綁定index計算延遲時間,每個html標簽都有dataset,例如
<div data-index="80"></div>
//元素實例就可以通過dataset對象訪問這個index屬性
//每一個我們自定義的屬性都會用去除data-前綴的字符串作為屬性綁定到dataset上
可復用的過渡
過渡可以通過 Vue 的組件系統(tǒng)實現(xiàn)復用。要創(chuàng)建一個可復用過渡組件赂弓,你需要做的就是將 <transition> 或者 <transition-group> 作為根組件绑榴,然后將任何子組件放置在其中就可以了,兩個小例子大家仔細研究
Vue.component('my-special-transition', {
template: '\
<transition\
name="very-special-transition"\
mode="out-in"\
v-on:before-enter="beforeEnter"\
v-on:after-enter="afterEnter"\
>\
<slot></slot>\
</transition>\
',
methods: {
beforeEnter: function (el) {
// ...
},
afterEnter: function (el) {
// ...
}
}
})
函數(shù)式組件
Vue.component('my-special-transition', {
functional: true,
render: function (createElement, context) {
var data = {
props: {
name: 'very-special-transition',
mode: 'out-in'
},
on: {
beforeEnter: function (el) {
// ...
},
afterEnter: function (el) {
// ...
}
}
}
return createElement('transition', data, context.children)
}
})
動態(tài)過渡
在 Vue 中即使是過渡也是數(shù)據驅動的!動態(tài)過渡最基本的例子是通過 name 特性來綁定動態(tài)值盈魁。所有過渡特性都可以動態(tài)綁定翔怎,但我們不僅僅只有特性可以利用,還可以通過事件鉤子獲取上下文中的所有數(shù)據杨耙,因為事件鉤子都是方法赤套。這意味著,根據組件的狀態(tài)不同珊膜,你的 JavaScript 過渡會有不同的表現(xiàn)容握。
關于動態(tài)過渡總結一下就是,使用velocity庫,對于過渡的一些狀態(tài)(參數(shù))通過其他的一些特性可以進行動態(tài)改變實現(xiàn)不同的過渡效果,或者說在特定的時間段中過渡效果不盡相同,最后直接貼官方的實例代碼了,大腦快爆炸了
- v
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>
<div id="dynamic-fade-demo" class="demo">
Fade In: <input type="range" v-model="fadeInDuration" min="0" v-bind:max="maxFadeDuration">
Fade Out: <input type="range" v-model="fadeOutDuration" min="0" v-bind:max="maxFadeDuration">
<transition
v-bind:css="false"
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:leave="leave"
>
<p v-if="show">hello</p>
</transition>
<button
v-if="stop"
v-on:click="stop = false; show = false"
>Start animating</button>
<button
v-else
v-on:click="stop = true"
>Stop it!</button>
</div>
- m
new Vue({
el: '#dynamic-fade-demo',
data: {
show: true,
fadeInDuration: 1000,
fadeOutDuration: 1000,
maxFadeDuration: 1500,
stop: true
},
mounted: function () {
this.show = false
},
methods: {
beforeEnter: function (el) {
el.style.opacity = 0
},
enter: function (el, done) {
var vm = this
Velocity(el,
{ opacity: 1 },
{
duration: this.fadeInDuration,
complete: function () {
done()
if (!vm.stop) vm.show = false
}
}
)
},
leave: function (el, done) {
var vm = this
Velocity(el,
{ opacity: 0 },
{
duration: this.fadeOutDuration,
complete: function () {
done()
vm.show = true
}
}
)
}
}
})
可復用性 & 組合
混入
混入 (mixin) 提供了一種非常靈活的方式,來分發(fā) Vue 組件中的可復用功能车柠。一個混入對象可以包含任意組件選項剔氏。當組件使用混入對象時,所有混入對象的選項將被“混合”進入該組件本身的選項竹祷。
var myMixin = {
created() {
this.hello();
},
methods: {
hello: function () {
console.log("hello")
}
}
}
var component = Vue.extend({
mixins:[myMixin]
})
new component();
這就是一個Vue的基本混入代碼了
選項合并
當組件和混入對象含有同名選項時谈跛,這些選項將以恰當?shù)姆绞竭M行“合并”。比如塑陵,數(shù)據對象在內部會進行遞歸合并感憾,并在發(fā)生沖突時以組件數(shù)據優(yōu)先。
var myMixin = {
data(){
return {
msg:"hello",
item:[1,2,3,4],
name:'suiyue'
}
}
}
var component = Vue.extend({
mixins:[myMixin],
data:function(){
return {
name:"小鋼炮",
age:9,
sex:"男"
}
},
created() {
console.log(this.$data);
}
})
上面的代碼輸出結果
同名鉤子函數(shù)將合并為一個數(shù)組令花,因此都將被調用阻桅。另外,混入對象的鉤子將在組件自身鉤子之前調用兼都。
var myMixin = {
created(){
console.log("混入的鉤子函數(shù)被調用");
}
}
var component = Vue.extend({
mixins:[myMixin],
data:function(){
return {
name:"小鋼炮",
age:9,
sex:"男"
}
},
created() {
console.log("組件鉤子函數(shù)被調用");
}
})
new component();
值為對象的選項鳍刷,例如 methods、components 和 directives俯抖,將被合并為同一個對象输瓜。兩個對象鍵名沖突時,取組件對象的鍵值對芬萍。
var myMixin={
methods: {
foo(){
console.log("foo")
},
sayHello(){
console.log("say Hello")
}
}
}
new Vue({
mixins:[myMixin],
methods:{
bar(){
console.log("bar");
},
sayHello(){
console.log("hello")
}
},
created() {
this.foo();
this.bar();
this.sayHello(); //hello
},
}).$mount("#app");
注意:Vue.extend() 也使用同樣的策略進行合并尤揣。
全局混入
混入也可以進行全局注冊。使用時格外小心柬祠!一旦使用全局混入北戏,它將影響每一個之后創(chuàng)建的 Vue 實例。使用恰當時漫蛔,這可以用來為自定義選項注入處理邏輯嗜愈。
// 為自定義的選項 'myOption' 注入一個處理器旧蛾。
Vue.mixin({
created: function () {
var myOption = this.$options.myOption
if (myOption) {
console.log(myOption)
}
}
})
請謹慎使用全局混入,因為它會影響每個單獨創(chuàng)建的 Vue 實例 (包括第三方組件)蠕嫁。大多數(shù)情況下锨天,只應當應用于自定義選項,就像上面示例一樣剃毒。推薦將其作為插件發(fā)布病袄,以避免重復應用混入。
自定義選項合并策略 看不懂給個鏈接吧
自定義指令
除了核心功能默認內置的指令 (v-model 和 v-show)赘阀,Vue 也允許注冊自定義指令益缠。一個簡單的注冊自定義指令的代碼如下
// 注冊一個全局自定義指令 `v-focus`
Vue.directive('focus', {
// 當被綁定的元素插入到 DOM 中時……
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
其中使用到了指令中的鉤子函數(shù),如果想注冊局部指令,組件中也接受一個 directives 的選項:
directives: {
focus: {
// 指令的定義
inserted: function (el) {
el.focus()
}
}
}
鉤子函數(shù)
- bind:只調用一次基公,指令第一次綁定到元素時調用幅慌。在這里可以進行一次性的初始化設置。
- inserted:被綁定元素插入父節(jié)點時調用 (僅保證父節(jié)點存在轰豆,但不一定已被插入文檔中)欠痴。
- update:所在組件的 VNode 更新時調用,但是可能發(fā)生在其子 VNode 更新之前秒咨。指令的值可能發(fā)生了改變喇辽,也可能沒有。但是你可以通過比較更新前后的值來忽略不必要的模板更新雨席。
- componentUpdated:指令所在組件的 VNode 及其子 VNode 全部更新后調用菩咨。
- unbind:只調用一次,指令與元素解綁時調用陡厘。
鉤子函數(shù)參數(shù)
指令鉤子函數(shù)會被傳入以下參數(shù):
-
el
:指令所綁定的元素抽米,可以用來直接操作 DOM 。 -
binding
:一個對象糙置,包含以下屬性:-
name
:指令名云茸,不包括v-
前綴。 -
value
:指令的綁定值谤饭,例如:v-my-directive="1 + 1"
中标捺,綁定值為2
。 -
oldValue
:指令綁定的前一個值揉抵,僅在update
和componentUpdated
鉤子中可用亡容。無論值是否改變都可用。 -
expression
:字符串形式的指令表達式冤今。例如v-my-directive="1 + 1"
中闺兢,表達式為"1 + 1"
。 -
arg
:傳給指令的參數(shù)戏罢,可選屋谭。例如v-my-directive:foo
中脚囊,參數(shù)為"foo"
。 -
modifiers
:一個包含修飾符的對象桐磁。例如:v-my-directive.foo.bar
中悔耘,修飾符對象為{ foo: true, bar: true }
。
-
-
vnode
:Vue 編譯生成的虛擬節(jié)點所意。移步 VNode API 來了解更多詳情。 -
oldVnode
:上一個虛擬節(jié)點催首,僅在update
和componentUpdated
鉤子中可用扶踊。
除了
el
之外,其它參數(shù)都應該是只讀的郎任,切勿進行修改秧耗。如果需要在鉤子之間共享數(shù)據,建議通過元素的 dataset 來進行舶治。
動態(tài)指令參數(shù)
在我們使用自定義指令的時候我們可以動態(tài)的傳遞參數(shù)給我們定義的指令
Vue.directive('pin', {//邏輯
})
我使用的時候,v-pin:[myData],那么這里的myData就可以在頁面刷新的時候進行動態(tài)的更新,那么指令中的處理代碼也必須考慮到所有的可能了
函數(shù)簡寫
在很多時候分井,你可能想在 bind 和 update 時觸發(fā)相同行為,而不關心其它的鉤子霉猛。比如這樣寫:
//當觸發(fā)了bind和update兩個鉤子的時候調用此函數(shù)
Vue.directive('color-swatch', function (el, binding) {
el.style.backgroundColor = binding.value
})
對象字面量
如果指令需要多個值尺锚,可以傳入一個 JavaScript 對象字面量。記住惜浅,指令函數(shù)能夠接受所有合法的 JavaScript 表達式瘫辩。
<div v-demo="{ color: 'white', text: 'hello!' }"></div>
Vue.directive('demo', function (el, binding) {
console.log(binding.value.color) // => "white"
console.log(binding.value.text) // => "hello!"
})
渲染函數(shù)&JSX
基礎
Vue 推薦在絕大多數(shù)情況下使用模板來創(chuàng)建你的 HTML。然而在一些場景中坛悉,你真的需要 JavaScript 的完全編程的能力伐厌。這時你可以用渲染函數(shù),它比模板更接近編譯器裸影。
new Vue({
data() {
return {
msg:"Hello Vue"
}
},
// vue可選的render函數(shù)替換掉默認的模板
render(createElement) {
return createElement('h1',this.msg)
},
}).$mount("#app");
如果會react的應該知道,render接受的那個方法跟babel轉換的jsx語法非常的相似,甚至感覺就是一樣的什么都沒變(所以這會不會帶來某些驚喜)
createElement 參數(shù)
createElement的函數(shù)的參數(shù)可以說是非常復雜,這里我具體給大家直接在一個例子中列出來,我只能說我盡量將注釋寫出來了,我表示有點懵了,真的有點復雜啊
<body>
<div id="app">
</div>
<script src="../lib/vue.js"></script>
<script>
// 我反正是對這個createEl函數(shù)懵了,不知道你呢?
new Vue({
data() {
return {
msg: "Hello Vue"
}
},
// vue可選的render函數(shù)替換掉默認的模板
render(createElement) {
return createElement(
// 第一個參數(shù)
// {String | Object | Function}
// 可以使一個html標簽名,或者一個組件選項對象,或者一個函數(shù)返回了前兩種(可以是異步)
// {
// // 我這里寫一個組件選項對象,這應該是最難理解的吧
// template:"<div>{{this.custom_data}}</div>",
// data(){
// return {
// custom_data:"我喜歡的事物"
// }
// }
// // 應該懂了吧
// },
// 上面注釋的就是第一個參數(shù)object對象形式,我這里直接寫字符串是因為,用了object方式無法正確渲染第三個參數(shù)提供的子級Vnode
// 因為提供了Object就只能在template中添加了
'div',
// 第二個參數(shù),用于綁定一些html特性,也允許綁定如 innerHTML 這樣的 DOM 屬性 (這會覆蓋 v-html 指令).
{
// 綁定類名
class: {
// 添加一個foo類
foo: true,
bar: false
},
// 綁定樣式
style: {
width: "100px",
height: "200px",
border: "1px solid red",
},
// 普通的html標簽屬性,如a的href,input的type等等
attrs: {
},
// 組件的props
props: {
// 我這里不傳入任何參數(shù)所以就不寫props了
},
// dom屬性
domProps: {
innerHTML: "這是通過domProps對象設置的文字"
},
// 事件監(jiān)聽到on屬性中,不在支持修飾符,參數(shù)
on: {
click() {
}
},
// 僅用于組件挣轨,用于監(jiān)聽原生事件,而不是組件內部使用
// `vm.$emit` 觸發(fā)的事件轩猩。
nativeOn: {
},
// 自定義指令卷扮。注意,你無法對 `binding` 中的 `oldValue`
// 賦值均践,因為 Vue 已經自動為你進行了同步画饥。
directives: [
{
name: 'my-custom-directive',
value: '2',
expression: '1 + 1',
arg: 'foo',
modifiers: {
bar: true
}
}
],
// 作用域插槽的格式為
// { name: props => VNode | Array<VNode> }
scopedSlots: {
default: props => createElement('span', props.text)
},
// 如果組件是其它組件的子組件固翰,需為插槽指定名稱
slot: 'name-of-slot',
// 其它特殊頂層屬性
key: 'myKey',
ref: 'myRef',
// 如果你在渲染函數(shù)中給多個元素都應用了相同的 ref 名摘完,
// 那么 `$refs.myRef` 會變成一個數(shù)組。
refInFor: true
},
// 第三個參數(shù) {String | Array}
// 子級虛擬節(jié)點 (VNodes)闷尿,由 `createElement()` 構建而成葫慎,
// 也可以使用字符串來生成“文本虛擬節(jié)點”衔彻∞背瑁可選。
// 可以是字符串或者數(shù)組,代表子級Vnode
[
'我是子級文本節(jié)點',
createElement('p', '我是子級p標簽')
]
);
},
}).$mount("#app");
// => "hello!"
</script>
</body>
約束這個直接留個鏈接在這里吧
使用 JavaScript 代替模板功能也留個鏈接
(為什么留兩個鏈接,這誰沒事記這么多屬性的createElement函數(shù)啊,直接用JSX了好吧)
函數(shù)式組件
我們可以將組件標記為
functional
艰额,這意味它無狀態(tài) (沒有響應式數(shù)據)澄港,也沒有實例 (沒有this
上下文)。
一個函數(shù)式組件如下:
Vue.component('my-component', {
functional: true,
// Props 是可選的
props: {
// ...
},
// 為了彌補缺少的實例
// 提供第二個參數(shù)作為上下文
render: function (createElement, context) {
// ...
}
})
注意:在 2.3.0 之前的版本中柄沮,如果一個函數(shù)式組件想要接收 prop回梧,則 props 選項是必須的。在 2.3.0 或以上的版本中祖搓,你可以省略 props 選項狱意,所有組件上的特性都會被自動隱式解析為 prop。
在2.5以上的版本中,如果你使用了類似于webpack之類的打包工具拆分每一個單獨的組件(*.vue文件),那么在每一個單獨的.vue文件中生命函數(shù)式組件方法
<template functional>
</template>
組件需要的一切都是通過 context 參數(shù)傳遞拯欧,它是一個包括如下字段的對象:
-
props
:提供所有 prop 的對象 -
children
: VNode 子節(jié)點的數(shù)組 -
slots
: 一個函數(shù)详囤,返回了包含所有插槽的對象 -
scopedSlots
: (2.6.0+) 一個暴露傳入的作用域插槽的對象。也以函數(shù)形式暴露普通插槽镐作。 -
data
:傳遞給組件的整個數(shù)據對象藏姐,作為createElement
的第二個參數(shù)傳入組件 -
parent
:對父組件的引用 -
listeners
: (2.3.0+) 一個包含了所有父組件為當前組件注冊的事件監(jiān)聽器的對象。這是data.on
的一個別名该贾。 -
injections
: (2.3.0+) 如果使用了inject
選項羔杨,則該對象包含了應當被注入的屬性。
那么關于函數(shù)式組件大家還需要努力研究,你會發(fā)現(xiàn),Vue的component方法生命函數(shù)式組件用的就是render方法,這個重的createElement方法還是得看看,但是函數(shù)式組件是直接給你封裝好了
<body>
<div id="app">
<my-func-com class="foo" :style="{color:'red',fontSize:'25px'}">
我可以直接在這里定義
<h1>Hello Vue</h1>
</my-func-com>
</div>
<script src="../lib/vue.js"></script>
<script>
Vue.component("my-func-com", {
functional: true,
render(h, context) {
return h(
'div',
context.data,
context.children);
},
})
new Vue({
data() {
return {
msg: "Hello Vue"
}
}
}).$mount("#app");
// => "hello!"
</script>
</body>
所以呢?其實Vue都給你把這些東西封裝好了,我們了解一下這個ugly的createElement函數(shù)就行了...
插件
使用插件
通過全局方法 Vue.use() 使用插件杨蛋。它需要在你調用 new Vue() 啟動應用之前完成:
// 調用 `MyPlugin.install(Vue)`
Vue.use(MyPlugin)
new Vue({
// ...組件選項
})
也可以傳入一個可選的選項對象:
Vue.use(MyPlugin, { someOption: true })
Vue.use 會自動阻止多次注冊相同插件问畅,屆時即使多次調用也只會注冊一次該插件。
Vue.js 官方提供的一些插件 (例如 vue-router
) 在檢測到 Vue
是可訪問的全局變量時會自動調用 Vue.use()
六荒。然而在像 CommonJS 這樣的模塊環(huán)境中护姆,你應該始終顯式地調用 Vue.use()
:
// 用 Browserify 或 webpack 提供的 CommonJS 模塊環(huán)境時
var Vue = require('vue')
var VueRouter = require('vue-router')
// 不要忘了調用此方法
Vue.use(VueRouter)
awesome-vue 集合了大量由社區(qū)貢獻的插件和庫
過濾器
Vue.js 允許你自定義過濾器,可被用于一些常見的文本格式化掏击。過濾器可以用在兩個地方:雙花括號插值和 v-bind 表達式 (后者從 2.1.0+ 開始支持)卵皂。過濾器應該被添加在 JavaScript 表達式的尾部,由“管道”符號指示:
<!-- 在雙花括號中 -->
{{ message | capitalize }}
<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>
你可以在一個組件的選項中定義本地的過濾器:
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
或者在創(chuàng)建 Vue 實例之前全局定義過濾器:
Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
new Vue({
// ...
})
當全局過濾器和局部過濾器重名時砚亭,會采用局部過濾器灯变。
過濾器函數(shù)總接收表達式的值 (之前的操作鏈的結果) 作為第一個參數(shù)。在上述例子中捅膘,capitalize 過濾器函數(shù)將會收到 message 的值作為第一個參數(shù)添祸。
過濾器可以串聯(lián):
{{ message | filterA | filterB }}
過濾器是 JavaScript 函數(shù),因此可以接收參數(shù):
{{ message | filterA('arg1', arg2) }}
這里寻仗,filterA 被定義為接收三個參數(shù)的過濾器函數(shù)刃泌。其中 message 的值作為第一個參數(shù),普通字符串 'arg1' 作為第二個參數(shù),表達式 arg2 的值作為第三個參數(shù)耙替。
老規(guī)矩,一個總結結束
過渡&動畫
vue提供的過渡標簽
transition
transition-group
過渡的6個類名
- v-enter
- v-enter-to
- v-enter-active
- v-leave
- v-leave-to
- v-leave-active
transition可以通過name屬性替換類名的v,同時也可以通過對應的屬性直接制定不同時間段的類名用于第三方動畫庫
- enter-class --- v-enter
- enter-to-class(2.1.8+) --- v-enter-to
- enter-active-class --- v-enter-active
- leave-class --- v-leave
- leave-to-class(2.1.8+) --- v-leave-to
- leave-active-class --- v-leave-active
transition同時提供了一個duration屬性,用于指定動畫的完成事件,可以直接指定一個數(shù)字(ms),同時可以是一個對象
{enter:'',leave:''}
,這里會有一個干嘣和duration大于實際動畫時間延遲執(zhí)行另外的動畫切記
動畫的鉤子函數(shù)
- before-enter
- enter
- after-enter
- enter-cancelled
- before-leave
- leave
- after-leave
- leave-cancelled
每一個鉤子函數(shù)都有可選的一個參數(shù)el,用于指向正在動畫的實例,同時enter和leave也有一個可選done回調(接收done要承擔接收done的責任)
初始渲染過渡
就是元素的第一次渲染的進場過渡,只會在第一次渲染時執(zhí)行一次
- appear ---- 必加屬性
- appear-class
- appear-to-class
- appear-active-class
appear沒有默認的v-appear一說啊,必須使用上面的transition屬性指定類名才行
當然進場過渡也有鉤子函數(shù)
- before-appear
- appear
- after-appear
- appear-cancelled
多個元素的過渡
大家一定要記住,多個元素如果有相同的標簽名的元素過渡必須加上不同的key否則無過渡效果
transition默認進場離場動畫是同時進行的,我們可以通過制定mode
屬性可選的先進后出(in-out)還是先出后進(out-in)
多個組件間的過渡
通過Vue提供的component
組件通過動態(tài)改變is屬性進行組件間的過渡切換
列表過渡
transiton-group
這個組件內部的元素每一個都必須有一個唯一的key否則不顯示數(shù)據,并且記住那個超級大坑
排序過渡 v-move特性,name同樣可以改變前面的v
交錯過渡 原本同步動畫的元素通過設置不同時間的延遲錯開時間進行動畫
可復用過渡 直接將transition封裝到一個組件中,這樣就實現(xiàn)了一個可以復用的過渡,可以是函數(shù)式組件和類組件,直接通過鉤子函數(shù)設置過渡的一些樣式的改變
動態(tài)過渡 通過JavaScript動態(tài)設置過渡的一些樣式,可以無時無刻或者在不同的時間點過渡效果不進相同的動畫
可復用行&組合
混入 Vue實例組件中都有mixins屬性,所以可以在組件或者vue實例中混入,如果有相同的選項會被合并掉,如果發(fā)生沖突優(yōu)先選擇組件內部定義的數(shù)據,同名生命周期鉤子函數(shù)將被合并為一個數(shù)組,觸發(fā)對應函數(shù)兩個函數(shù)都會被調用先觸發(fā)混入的鉤子或者生命周期函數(shù)
全局混入 使用Vue.mixin進行全局混入,全局混入會影響每一個Vue實例,new Vue啊還是組件啊都會影響
自定義指令
全局注冊
Vue.directive
局部注冊每一個Vue實例中的directives
選項
指令的鉤子函數(shù)
- bind
- inserted
- update
- componentUpdated
- unbind
鉤子函數(shù)參數(shù)
- el
- binding
- name
- value
- oldValue
- expression
- arg
- modifiers
- vnode --> 虛擬節(jié)點 --> 所有的虛擬節(jié)點組成虛擬DOM
- oldVnode
動態(tài)指令參數(shù) 我們可以直接使用自定義指令動態(tài)綁定指令的參數(shù) v-pin:[],當然組件中必須有處理參數(shù)所有可能性的邏輯
函數(shù)簡寫 可以直接在聲明指令的時候傳入一個函數(shù),這個函數(shù)會在bind和update時調用
對象字面量 可以給指令的value傳入一個對象,進入指令直接binding.value.xxx調用對應的參數(shù)
渲染參數(shù)&JSX
可以使用一個可選的render函數(shù)替換Vue默認的template,render函數(shù)有一個必須的createElement函數(shù),這個函數(shù)極其復雜,大家直接看上面寫的吧
函數(shù)式組件,函數(shù)式組件使用render實現(xiàn)的,但是函數(shù)式組件封裝了render方法,提供了第二個參數(shù)context,這個context封裝了createElement需要的值所以我們不必寫createElement的所有參數(shù)了,也不需要記,webpack打包環(huán)境生命函數(shù)式組件只需要在template標簽上添加一個functional
就行了
context參數(shù)包括如下對象
- props --> obj
- children -->arr
- slots -->func
- scopedSlots(2.6+)
- data --> obj
- parent
- listeners(2.3+) obj
- injections(2.3+)
插件:直接使用Vue.use()就可以使用插件
過濾器
過濾器也分全局和局部注冊,全局:
Vue.filter
局部:filters
,過濾器就是可以用來在數(shù)據被插入頁面的最后一層過濾,比如說我們可以使用過濾器來格式化時間啊變成 (yyyy-mm-dd)這種格式
這里想說的一些話,大概花了3天的時間通看了一遍vue的官方文檔記下此筆記,可能大家看完會發(fā)現(xiàn)為什么大多數(shù)特性都只有代碼,我只想說對我而言大多數(shù)地方看代碼就知道怎么回事了,就像有的地方,我無法解釋清楚就會直接貼上代碼,就明白了,就像我看vue官方文檔一樣,有些東西vue官方說的很那啥的一看代碼就懂了,還有就是為什么寫下這個筆記,因為vue官方文檔可能寫得有點權威吧,有很多東西都是一筆帶過,那么我對于某些東西都說的很大白了,也將踩過的坑給記錄了下來,還有就是貼代碼同時也代表學習一定要敲一遍,只不過有些地方貼的官方的,有點那啥的!目前還有vue-router,vuex的學習筆記準備開寫中,希望大家繼續(xù)支持我!
(如果你想跟我討論前端或者對于前端學習有什么疑惑的可以加一下此QQ群:78484-----5854
----> 感謝支持)