Vue快速上門(2)-模板語法

安木夕發表於2022-12-12

image.png

VUE家族系列:

  • Vue快速上門(1)-基礎知識
  • Vue快速上門(2)-模板語法
  • Vue快速上門(3)-元件與複用

01、模板語法

1.1、template模板

<template>是H5新增的模板元素,是一個用於HTML模板內容的包裝元素,主要使用其內部的內容。在普通的HTML頁面中,模板會出現在Dom樹中,但不會渲染,裡面的請求、指令碼也不會執行。<template>是Vue裡主要的模板定義方式,除此以外,常用的定義Vue.template方式:

Vue.template 描述 示例
<template>模板 H5的模板元素,其內容作為模板內容, <template id="tp">
<script>模板 在一個 <script> 元素中定義模板內容,裡面的內容都會被當成字串。透過#id引用 <script type="text/x-template" id="xtp">
字元模板 用HTML字串直接定義模板內容 template: '<p>{{name}}</p>'
內聯模板inline-template 用在子元件上,用這個子元件元素裡面的內容作為模板,而不是渲染他的原本的內容。這個一般不常用吧,作用域有點亂。 <mycom inline-template> </mycom>

?Vue的template選項值

  • HTML語法:模板內容就是普通的HTML語法,Vue新增了一些繫結資料的指令。
  • 根元素:內部都必須有一個根元素<template>本身是沒啥用的,主要用的是其內部InnerHTML。
  • #值:如果值以 # 開始,則它將被用作id選擇符,並使用匹配元素的 innerHTML 作為模板。
  • 優先於el:根據上文中Vue生命週期流程可知,template內容會優先於el.outerHTML內容,編譯為渲染render。
<div id="app5">
    <h2>app</h2>
    <template>
        <p>vue中的template的innerHtml會被正常渲染</p>
    </template>
    <user-com inline-template>
        <p>內聯模板,替代原有內容</p>
    </user-com>
</div>
<script>
    let app5 = new Vue({
        el: "#app5", 
        template: '#tp',  //<template id="tp">模板
        template: '#xtp',  //<script type="text/x-template" id="xtp">模板
        template: '<p>{{mes}}--字元模板</p>', //字元模板
        template: '<p>{{mes}}</p><span>❌</span>', //❌報錯:Component template should contain exactly one root element
        data: { mes: "message:hello!" },
        components: {
            'user-com': {
                data: function () { return { name: "same" } },
                template: '<p>{{name}}-這是一個子元件</p>',
            }
        }
    })
</script>

1.2、文字插值

Vue的模板語法基於HTML的語法,可以在模板中申明式的繫結例項資料、事件方法。在Vue中,模板被編譯成虛擬Dom渲染函式,先在虛擬Dom上進行操作,這樣可以最最佳化Dom及操作次數,然後再渲染到正式的Dom中。常用的一種資料繫結方式為—— 文字插值

?文字插值{{ data }},“Mustache”語法 (雙大括號) (/ˈmʌstæʃ; məˈstɑːʃ/ 鬍子),內容不支援html標籤、不支援繫結。

?注意:安全第一,不要用輸入的內容來插值,容易導致XSS攻擊。

<div id="app3">
    <div>div1:{{html}}
        <p>{{mes}}</p>
        <p>{{birthday}}</p>
    </div>
    <div v-text="html">div2:</div>
    <div v-html="html">div3:</div>
</div>
<script>
    let vm2 = new Vue({
        el: "#app3",
        data: {
            html: "<span style='color:red'>red span{{mes}}</span>",
            mes: "hello world!",
            birthday: '2000-12-11',
        },
    });
</script>

image.png

1.3、JavaScript表示式

{{文字插值}}v-bind繫結、v-on事件繫結等指令中都支援完全的JavaScript的表示式,只能是單個表示式語句,不支援複雜語句、迴圈控制。

<div id="app3">
    <div>
        <p v-bind:style="'color:'+fcolor">{{mes.split(' ').join('---')}}</p>
        <p>age:{{(new Date().getFullYear() - new Date(birthday).getFullYear())}}</p>
        <p>age:{{((new Date() - new Date(birthday))/3600/24/365/1000).toFixed(1)}}</p>
    </div>
</div>
<script>
    let vm2 = new Vue({
        el: "#app3",
        data: {
            mes:"hello world!",
            birthday:'2000-12-11',
            fcolor:'red',
        },
    });
</script>

image.png


02、模板指令

2.1、指令大全?

指令格式:指令:引數.修飾符 = "值"

Vue指令/ 簡寫 描述 示例
v-text= 繫結 textContent,同{{文字插值}}(如JS延遲,有閃爍) <span v-text="msg"></> = <span>{{msg}}</>
v-html= 繫結 innerHTML,內容支援html標籤(不支援繫結),需注意安全性。其他插值指令的值都不支援HTML標籤,會被轉義。 <div v-html="html"></div>
v-bind: = / := 繫結元素屬性值,動態地繫結一個或多個 attribute,或元件prop <img v-bind:src="img" :class="imgStyle">
v-model= 表單元素的值雙向繫結,不支援表示式 <input type="date" v-model="birthday">
v-show= 值為真元素顯示(切換 display),適用於頻繁切換顯示。 <div v-show="value =='方案1'">方案1</div>
v-if= 值為真才渲染,否則不會建立或銷燬已有元素,支援<template> <img v-if="growth > 0" alt="上升">
v-else-if= v-if else if塊,可連續使用,跟在v-if/v-else-if後面 <img v-else-if="growth < 0" alt="下降">
v-else v-if 或者 v-else-if 新增的“else 塊”,無引數 <img v-else alt="穩定">
v-for= 列表渲染item in/of expression,支援index索引、物件 <li v-for="item in items">{{item}}</li>
v-on: = / @= 繫結事件,引數為事件名;值為方法名,或內聯語句func($event) <button v-on:click="submit" @click="">
:key= 元素唯一key值,配合v-for使用,避免重複key更新異常,用於虛擬Dom中判斷新舊元素。或用於強制元素替換而不復用 ul v-for="item in list" :key="item.id">
<i:key="text">{{text}}</i> //每次都是新元素
v-once 只更新元素/元件一次,後續更新都視為靜態內容,不再更新 <p v-text="message" v-once></p>
v-pre 不編譯,跳過這個元素和它的子元素的編譯過程,顯示原生的內容 <div v-pre> </div>
is 動態的繫結一個元件,值為元件名稱/Vue選項物件 <component is="user-info"></component>
v-slot:= / #:= 具名插槽,有名分的插槽。指定插槽名稱,或繫結插槽ViewModel v-slot:header v-solt="{user}"
ref 給子元件、Dom元素註冊引用名,透過$refs訪問這些被ref標記的物件 元件上的ref指向其元件Vue例項
<div ref="left">
v-cloak 解決{{文字插值}}會閃爍的問題,鎖定[v-cloak]樣式直到編譯完後清除 [v-cloak]{ display: none;} <div v-cloak>

2.2、[動態引數]

2.6.0新增的[動態引數],指令的引數可以使用[動態引數]=一個JavaScript表示式。

<a v-on:[functype]="setStyle" v-bind:[href]="1+1">方案1</a>

  • 字串值:這裡的動態引數預期結果為一個字串,異常則為nullnull也可用於顯示的移除繫結。
  • 不要出現引號,包括空格、引號、<>/=,HTML的屬性中是非法的。
  • 引數名用小寫,不要用大寫,瀏覽器會強制轉換HTML屬性為小寫。如<a v-on:[funcType]="fswitch">方案1</a> 包含大寫的屬性引數會找不到對應值。
<div id="app">
    <img v-bind:[getProp(file.type)]="file.url" alt="file" v-on:[eventType]="eventHandler">
    <img v-bind:[getprop1]="file.url" alt="file" v-on:[eventType]="eventHandler">
</div>
<script>
    let vm2 = new Vue({
        el: "#app",
        data: {
            file: { type: 'img', url: '../../res/bg.gif' },
            eventType: 'click', //大寫會找不到,報錯:Property or method "eventtype" is not defined
            eventtype: 'click',
        },
        computed:{ getprop1(){ return 'src'; } },
        methods: {
            getprop(type) {   //如果有大寫(getProp)就會找不到報錯:Property or method "getprop" is not defined
                switch (type) {
                    case 'img': return 'src';
                    case 'url': return 'href';
                }
            },
            eventHandler(e) { console.log(e.target.tagName); }
        }
    });
</script>

2.3、v-for 列表渲染

用迴圈建立多個元素/元件,迴圈物件可以是陣列、物件、常量,也可以是計算屬性方法表示式。迴圈表示式中可以用in,也可用of(沒有區別)。如果迴圈建立多個元素沒有根元素,則可用一個模板<template>來包裹,這時就不需要key了。

  • 列表迴圈v-for="item in items"
  • 列表迴圈-帶索引引數v-for="(item,index) in items"
  • 物件迴圈-valuev-for="value in user",Vue是按照Object.keys(obj) 的結果遍歷。
  • 物件迴圈-帶引數v-for="(value,name,index) in user"
<div id="app">
    <span v-for="n of 20">{{ n }} </span>
    <ul>
        <li><span v-for="item in items">{{item.name}} ; </span></li>
        <li><span v-for="(item,index) in items">{{index+1}}:{{item.name}} ; </span></li>
        <!--user物件-->
        <li><span v-for="value in user">{{value}} ; </span></li>
        <li><span v-for="(value,name,index) in user"><i>{{index+1}}</i>)<b>{{name}}</b>:{{value}} ; </span></li>
    </ul>
    <br>
</div>
<script>
    let vm2 = new Vue({
        el: "#app",
        data: {
            items: [{ name: 'sam' }, { name: 'zhangsan' }],
            user: { name: 'sam', age: 20, birthday: '2000-12-11' }
        },
    });
</script>

image.png

?列表更新策略(:key):Vue預設列表項原地更新,不管資料的順序。

如果希望更新時保持資料、元素的順序,及更新的準確性、高效能,則需要給元素設定一個唯一身份標識 key:v-bind:key="kid" :key="kid",值應為字元、數字

Vue預設是最大限度複用元素,虛擬Dom中用key來判斷新舊元素,重複key可能會導致更新異常,也可用於強制元素替換而不復用。一般推薦儘量提供key,他是Vue識別節點的通用機制(diff中的元素比較)。在<transition-group>列表動畫中,key是必須的。

? 注意

  • v-for迴圈中的引數的順序須一致,別名不重要。
  • v-for優先順序高於v-if,共用時需注意,可用來過濾不符合條件的項。

2.4、class樣式繫結

classstyle為HTML原本的attribute,透過v-bind繫結,增強:

  • 物件繫結,語法:{classA:bindbool, classB:bindbool},繫結值bindbool為真則樣式classA有效,方便基於判斷繫結多個class。除了直接物件表示式,還可以是繫結的物件、計算屬性返回的物件、普通表示式。
  • 陣列繫結,繫結多個樣式,陣列中可以是繫結值、表示式、常量樣式名。
  • 支援和原生class共存,會合並class值。包括元件也是如此,和元件的根元素class合併。

?一個tab切換示例:

<style>
    #app5 ul { list-style-type: none; margin: 0; padding: 0; text-align: center; }
    #app5 ul li { display: inline-block; margin: 0 10px; }
    #app5 ul li.active { background-color: antiquewhite; }
    .psection {
        display: none; background-color: antiquewhite;
        text-align: center; margin: 0; line-height: 50px;
    }
    .psection.active { display: block; }
</style>
<div id="app5">
    <ul>
        <li v-for="item in items" v-on:click="liclick(item)" v-bind:class="{active:item.isActive}">{{item.title}}</li>
    </ul>
    <div>
        <p v-for="item in items" v-bind:class="['psection',item.isActive?'active':'']">{{item.content}}</p>
        <!-- 也可以用v-show實現切換 v-show="item.isActive" -->
        <p v-for="item in items" v-show="item.isActive">{{item.content}}</p>
    </div>
</div>
<script>
    let app5 = new Vue({
        el: "#app5",
        data: {
            items: [{ title: "顯示1", content: "1", isActive: false }
                    , { title: "顯示2", content: "2", isActive: true }
                    , { title: "顯示3", content: "3", isActive: false }],
        },
        methods: {
            liclick: function (item) {
                this.items.forEach(item => item.isActive = false);
                item.isActive = true;
            },
        }
    })
</script>

image.png

2.5、style內聯樣式繫結

對於Style的繫結,Vue直接把style物件化了,透過物件化表示式,或者直接一個物件繫結(更清晰)。

  • 物件陣列:支援多個物件的陣列,合併物件中的style樣式規則。
  • 支援一定的相容性字首,如transition,Vue會自動檢測瀏覽器並新增相容性字首。
  • 物件屬性多重值,提供多個值的陣列display:['-ms-flexbox','flex'],Vue只會渲染陣列中最後一個被瀏覽器支援的值。
<div id="app5">
    <div>
        <!--陣列+物件混合-->
        <p :style="[{color:activeColor,'font-size':'20px'},marginStyle]"> p1 - Content </p>
        <p v-bind:style="marginStyle"> p2 - Content </p>  <!--物件-->
    </div>
</div>
<script>
    let app5 = new Vue({
        el: "#app5",
        data: {
            activeColor: 'red',
            marginStyle: { margin: "30px", transform: "rotate(10deg)", display: ['-ms-flexbox', 'flex'] },
        },
    })
</script>

2.6、v-on/@事件.修飾符

事件繫結格式:v-on:/@ 事件名.修飾符 = ""@v-on:的縮寫形式。事件名支援動態 [引數] 繫結。Vue的事件都是直接繫結到元素的,沒有用事件委託。

事件引數:方法預設支援事件引數物件Event,內聯JavaScript程式碼透過$event訪問事件物件。

<div id="app7">
    <!-- 只響應一次 + 右鍵點選 -->
    <button @click.once.right="arrClick">button</button>
    <li v-for="n in arr"><button v-on:click="arrClick($event,n)">{{n}}</button></li>
    <!-- 左方向按鍵,數值增1 -->
    <input type="text" v-on:keydown.preven.arrow-left="num=parseInt(num)+1" v-model="num">
</div>
<script>
    let app7 = new Vue({
        el: "#app7",
        data: { arr: [1, 2, 3], num: 0 },
        methods: {
            arrClick: function (e, arg) { console.log(e?.target.tagName, arg ? arg : "") },
        }
    })
</script>

?修飾符 可以加強事件的能力,使用上可以串聯(注意順序),也可以只有修飾符。

修飾符 描述
.stop 呼叫 event.stopPropagation()停止向上冒泡(propagation /ˌprɒpəˈɡeɪʃn/ 傳播)
.preven 呼叫 event.preventDefault()取消預設事件行為,如checkbox、<a>的預設事件行為,不影響冒泡
.self 只當事件是從偵聽器繫結的元素本身觸發時才觸發回撥,只能自身觸發,內部冒泡事件的不會響應。
.once 只觸發一次回撥
.capture 新增事件偵聽器時使用 capture(捕獲)模式,事件流的捕獲階段就觸發事件。
.passive (2.3.0) 以 { passive: true } 模式新增偵聽器,passive 設為 true 可以啟用效能最佳化,主要是針對滾動、觸控相關事件。參考MDN:使用 passive 改善滾屏效能,(passive /ˈpæsɪv/被動,消極)。
.native 監聽元件根元素的原生事件
滑鼠鍵修飾符 .left:只當點選滑鼠左鍵時觸發。.right:滑鼠右鍵觸發;.middle:滑鼠中鍵觸發
輔助鍵修飾符 配合滑鼠、按鍵事件使用:ctrl、shift、alt、meta(windows鍵,或command)
鍵盤事件修飾符 針對事件keydownkeyupkeypress按鍵修飾符:
● esc、tab、space、enter、delete、up、down、left、right、a、b、c字母鍵等。
.{keyCode &#124; keyAlias}按鍵修飾符,只當事件是從特定鍵觸發時才觸發回撥。可使用 KeyboardEvent.key值轉換為 kebab-case 來格式使用。

2.7、v-model表單繫結

使用v-model在表單元素<input><textarea><select>上建立雙向繫結,會忽略他們本身的值屬性(value、checked、selected),所以注意設定預設值。分組的單選、多選元素組按照name進行分組即可,一組的繫結也是一樣的。

基本原理就是監聽表單的輸入事件,實現檢視到資料的同步:

  • texttextarea 元素使用 value propertyinput事件。
  • checkboxradio 使用 checked propertychange 事件。name用於分組名稱空間,value作為選中的值。
  • select 欄位將 value 作為 prop 並將 change 作為事件。

?v-model 修飾符

  • .lazychange 事件同步資料,主要針對text類表單元素,失去焦點時才會觸發change事件。
  • .number:強制轉換值為數值型別,需配合type="number"使用,如果無法 parseFloat() 解析則返回原始值。
  • .trim:去掉首尾空格,比較實用!
<div id="app8">
    <div>
        <label>姓名:<input type="text" v-model.lazy.trim="name"></label>
        <label>姓名:<input type="number" v-model.number="age">{{age}}</label>
    </div>
    <div>
        <span>性別:</span>
        <template v-for="(value,name) in dataset.sex">
            <label><input type="radio" v-model.lazy="sex" name="sex" :value="name">{{value}}</label>
        </template>
        <i>結果:{{sex}}</i>
    </div>
    <div>
        <span>技能:</span>
        <label v-for="item in dataset.skill"><input type="checkbox" v-model="skill" :value="item"
                                                 name="skill">{{item}}</label>
        <i>結果:{{skill}}</i>
    </div>
    <div>
        <span>性別-選擇框:</span>
        <select name="ssex" v-model="sex">
            <option disabled value="">請選擇</option>
            <option v-for="(value,name) of dataset.sex" v-once :value="name">{{value}}</option>
        </select>
        <i>結果:{{sex}}</i>
    </div>
    <div>
        <span>技能-選擇框:</span>
        <select name="sskill" v-model="skill" multiple>
            <option disabled value="">請選擇</option>
            <option v-for="item in dataset.skill" v-once :value="item">{{item}}</option>
        </select>
        <i>結果:{{skill.join()}}</i>
    </div>
</div>
<script>
    let app8 = new Vue({
        el: "#app8",
        data: {
            name: '', age: 0, checked: false, sex: '', skill: ["開機"],
            dataset: {
                sex: { male: '男', female: "女", other: '其他' },
                skill: ['開機', '關機', '寫Bug', '吃飯'],
            }}
    })
</script>

image.png

2.8、關於資料丟失

基於Vue對資料監聽的機制,Vue 不能檢測陣列和物件的變化。因此有些場景資料無法實現變更監聽,就無法同步檢視了,造成“資料丟失”的現象。

  • 陣列透過索引修改、新增的值無法監聽。
  • 未初始化的新成員,新增的物件新成員,沒有被初始化時監聽,app1.user.新屬性=20
  • 被凍結的物件無法監聽:Object.freeze(obj)
  • v-model 繫結的未定義屬性不丟失!因為他是用的$set去更新的,注意不能是data的一級屬性。

image.png

?解決辦法

  • 陣列整體賦值:新陣列賦值,或者用陣列方法修改資料,如push方法是被Vue代理實現了攔截的。

  • 在Vue初始化時先申明屬性。

  • vm.$set.(obj)強制更新,會先新增響應,然後再更新檢視。不建議動態新增data的一級屬性!在選項中預先申明。好像是一些邊界方面的技術原因。

<div id="app">
    <p>p1:
        <span v-for="n in arr">{{n}},</span>
    </p>
    <p>p2:
        <b>name:</b>{{user.name}},
        <b>age:</b>{{user.age}}
    </p>
</div>
<script>
    let app = new Vue({
        el: '#app',
        data: { arr: [1, 2, 3], user: { name: 'sam' }, }
    })
    app.arr[0] = 100;  //修改,無更新
    app.arr[app.arr.length] = 4; //新增,無更新

    app.$set(app.arr,0,101); //強制更新
    app.arr.push(5); //push有效,前面的也生效了,因為觸發了更新
    app.arr = [100, 2, 3];  //重新賦值陣列,有效
    app.arr = Array.of(...Array.from(app.arr),10,10); //重新賦值陣列,有效

    app.user.age = 10; //這是新的屬性,無更新
    app.$set(app.user,'age',18); //強制更新,有效
    app.user = Object.assign({}, app.user);
    app.user = user; //賦值物件,有效
</script>

03、Vue動畫

Vue提供了兩個內建元素,<transition><transition-group>,用來包裝單個元素、列表元素,輔助實現在元素顯示隱藏、建立刪除、移動過程中的過渡動畫效果。更高階、更復雜的動畫可以藉助第三方動畫元件來實現。

3.1、transition過渡動畫

Vue提供了一個<transition>封裝元件,用來包裝需要動畫的單個內容。Vue主要提供了一個比較基礎的動畫機制,幫你處理過渡的事件、動畫類呼叫。所以還是需要你自己來寫動畫的CSS類,或者動畫JS程式碼。

image

  • 三種型別的過渡:元素初始渲染過渡、元素進入過渡(顯示)、元素離開過渡(隱藏/刪除)。
  • 包裹單元素<transition>只能包裹一個跟元素(包括v-if、v-show切換),動畫應用在這個跟元素上,不會產生額外Dom元素。
  • 過河拆橋--事後清理:動畫完成後,CSS動畫資源會被清除,Vue會自動監聽transitionend,或animationend事件。
  • 過渡CSS類名規範:如下表格,按照CSS類名寫樣式,Vue自動呼叫。v為動畫元件<transition>name值,沒有則預設v
  • CSS過渡類attribute:作用同上,值是Class樣式類名,優先順序更高。CSS值可以自定義,也可以很方便的呼叫第三方CSS動畫庫。
進入過渡 離開過渡 CSS過渡類attribute,優先順序更高
v-enter:初始點狀態 v-leave:初始點狀態 enter-class、leave-class
v-enter-to:結束點狀態 v-leave-to:結束點狀態 enter-to-class、leave-to-class
v-enter-active:進入過程,設定動畫引數 v-leave-active:離開過程 enter-active-class、leave-active-class

?過渡動畫示例:CodePen

<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet">
<div id="app15">
    <div>
        <button v-on:click="acitve = !acitve" ref="btn">消失</button>
        <transition name="h" v-on:after-enter="updateBtn" v-on:after-leave="updateBtn">
            <p v-show="acitve" style="color:red">秦時明月漢時關,萬里長征人未還!</p>
        </transition>
        <!-- CSS過渡類Class,使用了animate元件-->
        <transition name="h" enter-active-class="animated fadeInRightBig" leave-active-class="animated zoomOut">
            <p v-show="acitve" style="color:blue">秦時明月漢時關,趕快下樓做核酸!</p>
        </transition>
    </div>
</div>
<style>
    .h-enter-active,
    .h-leave-active { transition: all 0.5s cubic-bezier(1.0, 0.8, 0.5, 1.1); }
    .h-enter, .h-leave-to { opacity: 0; }
    .h-enter { transform: translateX(80px); }
    .h-leave-to { transform: translateX(-80px); }
</style>
<script>
    let app15 = new Vue({
        el: "#app15",
        data: { acitve: true },
        methods: {
            updateBtn: function (e) { this.$refs.btn.innerText = this.acitve ? "消失" : "出來"; }
        }
    })
</script>

1.gif

transition動畫元素的屬性、鉤子事件:

✔️<transition>屬性 描述
name 用於自動生成 CSS 過渡類名:name-entername-leave
appear 開場動畫,是否在初始渲染時使用過渡,bool,預設為 false
css bool,是否使用 CSS 過渡類,預設ture。如果用鉤子函式JS控制動畫,可以關閉css。
type Vue 監聽過渡事件型別型別,用於動畫完成後清理class資源,animation (animationend) 或 transition(transitionend
mode 過渡模式,控制新舊元素進入/離開過渡的先後順序, out-inin-out,預設同時進行。
duration 過渡的持續時間(ms),{ enter: 500, leave: 500} ,用於事後清理資源,而不是設定動畫時間
✔️CSS 過渡類 attribute
進入過渡 enter-class、enter-to-class、enter-active-class
離開過渡 leave-class、leave-to-class、leave-active-class
初始渲染過渡 appear-class、appear-to-class、appear-active-class
✔️JavaScript 鉤子 用於JS的動畫控制,可以藉助第三方JS動畫庫
進入過渡 before-enter(el)、enter(el, done)、after-enter(el)、enter-cancelled(el)
當只用 JavaScript 過渡的時候,在 enter 和 leave 中必須使用 done 進行回撥。否則,它們將被同步呼叫,過渡會立即完成。
離開過渡 before-leave(el)、leave(el, done)、after-leave(el)、leave-cancelled (el)
leaveCancelled 只用於 v-show 中
初始渲染過渡 before-appear(el)、appear(el)、after-appear(el)、appear-cancelled(el)

3.2、transition-group列表過渡動畫

列表的過渡是針對多個元素,需要 transition-group元件,支援的過渡類、特性和事件和<transition>類似,多了tag移動過渡。不過過渡針對的不是一個根元素,而是內部的每一個一級元素。<transition-group>會產生一個元素<span>,可透過tag修改渲染的元素,比如用來代替<ul>不錯。

<transition-group> 描述
tag <transition-group>產生的元素標籤名,預設為span,可以根據需要設定。
✔️CSS 過渡類 attribute
移動過渡 move-class(類名:v-move)

如果只是用進入、離開過渡,會導致其他元素的位置變化比較生硬。使用移動過渡v-move/move-class,在元素改變定位的時候使用。Vue 使用了一個 FLIP 的動畫佇列,使用transforms將元素位置平滑移動。

?v-for 時需設定keyv-for建立列表元素時,強烈建議設定列表項的key值,列表動畫中這是強制要求了!

程式碼示例:CodePen

<h1>app15: 動畫</h1>
<div id="app15">
    <h3>列表動畫:<button @click="shuffle">隨機亂序</button></h3>
    <div>
        <transition-group tag="ul" name="list" enter-active-class="animated flipInX">
            <li v-for="(s,i) in list" v-bind:key="s">{{s}}
                <button @click="list.splice(i,1)">-</button> 
                <button @click="list.splice(i+1,0,s+index++)">+</button>
            </li>
        </transition-group>
    </div>
</div>
<style>
    .list-enter { opacity:0; }
    .list-enter-to{ transform: translateX(50px); }
    .list-leave-active{ transition: all 1s; }        
    .list-leave-to { opacity: 0; transform: translateX(50px); }
    .list-leave-active {
        position: absolute;   /*讓元素移動更順滑*/
        transition: all 1s; }
    .list-move { transition: 0.5s; }
</style>
<script>
    let app15 = new Vue({
        el: "#app15",
        data: { acitve: true, index: 1, list: ['張三', '李四', '王五', '對六', '小七', '李白'] },
        methods: {
            // 隨機排序
            shuffle: function () { this.list.sort((a, b) => Math.random() - 0.5) }
        }
    })
</script>

1.gif


©️版權申明:版權所有@安木夕,本文內容僅供學習,歡迎指正、交流,轉載請註明出處!原文編輯地址-語雀

相關文章