Vue文件中幾個易忽視部分的剖析

geekarl發表於2018-03-22

針對Vue文件中部分大家可能不會去研讀的內容,我做了個小總結,作為有經驗者的快餐,不是特別適合初學者,可能有不妥之處,希望大家多提建議。

節省程式碼量的mixin

mixin概念:元件級可複用邏輯,包括資料變數/生命週期鉤子/公共方法,從而在混入的元件中可以直接使用,不用重複寫冗餘邏輯(類似繼承)

使用方法:

  • 在某一公共資料夾pub下建立mixin資料夾,其下建立mixinTest.js
const mixinTest = {
    created() {
        console.log(`components ${this.name} created`)
    },
    methods: {
        hello() {
            console.log('mixin method hello')
        }
    }
}
export default mixinTest
複製程式碼
  • 在元件中引用剛才的公共混入檔案並使用
import mixinTest from '../pub/mixin/mixinTest.js'
export default {
    data() {
        return {
            name: 'hello'
        }
    },
    mixins: [mixinTest],
    methods: {
        useMixin() {
            this.hello()
        }
    }
}

複製程式碼

ps: 若是使用Vue.mixin()這個方法,則會影響之後建立的所有Vue示例,慎用!

注意mixin的幾個特性:

  1. 混入的資料變數是淺合併,衝突時以元件內的資料優先(物件裡面的自定義變數)
  2. 混入的生命週期函式內的邏輯會與元件內定義的生命週期函式邏輯進行合併,並且先執行(created/mounted/destroy)
  3. 混入的值為物件的選項,會混合成一個物件,衝突後也是以元件內鍵名優先(data/method/components/directives)

slot內容分發

slot概念引入:Vue跟React在寫法上的不同就在於元件與子元件內部元素的組織上,在元件裡面沒有children元素供我們訪問和展現(暫不考慮render函式),取而代之的API是slot

使用場景定義:

  • 自定義的子元件裡面有巢狀的HTML或者其他自定義的標籤元件
  • 這個自定義的子元件是寫在父元件裡面,巢狀的東西也放在父元件裡面
  • 通過在子元件的模板裡面使用<slot></slot>標籤,從而達到渲染寫在父元件裡的巢狀標籤的效果
  • 本質是把父元件放在子元件裡的內容,插到了子元件的位置,多個標籤也會一起被插入
<template>
    <div id="app">  
        <self-component>  <!--self-component表示自定義的元件-->
            <span>12345</span>  <!--父元件裡的巢狀標籤--> 
        </self-component>  
    </div> 
</template>
<script>
export default {
    components: [selfComponent]
}
</script>

<!--self-component的元件模板-->
<template>
    <div>
        <button><slot></slot></button>
    </div>
</template>
<script>
export default {
    // 只有子元件的模板裡面有slot標籤,才能取到寫在自定義元件裡面的標籤的渲染引用
}
</script>
複製程式碼

slot特性的進階兩點:

  1. slot插入內容的編譯作用域:被分發的內容的作用域,根據其所在模板決定
  • 具體內容寫的位置,決定了編譯的作用域(大部分情況都是在父元件作用域下)
  • 2.1.0+新增作用域插槽,從而可以把子元件的屬性暴露給父元件中寫在子元件內的內容使用
  • 子元件中的slot標籤可以直接寫自定義屬性,然後父元件寫在slot中的標籤加上slot-scope屬性
<!-- 父元件模板 -->
<child :items="items">
  <!-- 作用域插槽也可以是具名的 -->
  <li slot="item" slot-scope="props" class="my-fancy-item">{{ props.text }}</li>
</child>

<!-- 子元件模板 -->
<ul>
  <slot name="item" v-for="item in items" :text="item.text">
    <!-- 這裡寫當父元件引用子元件但沒寫內部內容時展示的東東 -->
  </slot>
</ul>
複製程式碼
  1. slot的name屬性來指定標籤插入的位置,也就是文件裡面的具名插槽(這個官方文件說的明白)
  • 在子元件的模板裡面寫的slot有個name屬性(<slot name="foo"></slot>)
  • 在父元件中寫子元件裡面的插槽內容,指明slot屬性(<p slot="foo">123</p>)
  • 父元件的內容就會對應slot==name放到正確的位置
  • 沒有指明slot屬性的就會預設放到匿名插槽的位置上

動態元件

動態元件這個特性,很多人寫的Vue專案也不少,但也沒用到過這個,有必要多說幾句

動態元件適用情況:

  1. 單頁應用,部分元件的切換不涉及路由,只是頁面有一塊區域的元件要變更
  2. 變更的元件引數定義上是一致的,比如都是對話方塊,要傳一個物件進去,但物件裡面的資料結構不同
  3. 通過使用component的is屬性,避免在template中的冗餘元件程式碼,避免多個v-if模板程式碼更加整潔

使用的方法(借鑑文件):

<keep-alive>
    <component v-bind:is="currentView">
    <!-- 元件在 vm.currentview (對應元件名稱)變化時改變! -->
    <!-- 非活動元件將被快取!可以保留它的狀態或避免重新渲染 -->
    </component>
</keep-alive>
複製程式碼

注意點:

  • 動態切換的元件都要引入到父元件中,渲染是動態的,但引入不是。
  • <keep-alive>包裹動態元件時,會快取不活動的元件例項,提高效能,避免重複渲染(keep-alive不會渲染額外DOM結構)
  • <keep-alive>includeexclude這兩個屬性,用於指定快取和不快取的元件(傳入字串/陣列/正則)
  • 另一種避免重新渲染的方法是為標籤增加屬性v-once,用於快取大量的靜態內容,避免重複渲染。

ps:<keep-alive>不會在函式式元件中正常工作,因為它們沒有快取例項。

動畫與過渡

其實很多前端工程師第一次用Vue的動畫和過渡都是通過庫元件來做到的,所以對這塊沒怎麼深挖,各種過渡特效和按鈕動畫就跑起來了,現在就看下文件,補補課

  前端實現動畫的基本方法分為三種種:css3的過渡和keyframe/javascript操縱dom/使用webgl或者canvas來獨立實現,其中第三種是作為展示動畫,與互動結合較少,而Vue作為一個框架,其支援動畫基是從前兩種入手的,從官方文件提到的四種支援就可以看出這一點。不過官方文件是從DOM過渡和狀態過渡兩個方面來講解,前者是DOM的消失和出現的動畫等屬性的變化,後者是頁面上某些值的變化。

DOM屬性的改變

  1. 若是單個元素/元件的顯隱,在元件外面包裹一層<transition></transition>,而後選擇是css過渡還是javascript過渡
  • CSS過渡:
  1. vue提供了六個樣式字尾,本質是在dom過渡的過程中動態地新增和刪除對應的className。(-[enter|leave]-?[active|to]?)
  2. 如果用css庫來輔助開發,可以在transiton這個標籤上定義自定義過渡類名,也是六個屬性。([enter|leave]-?[active|to]?-class)
  3. 常見的一種效果是元素首次渲染的動畫,如懶載入圖片飛入,這個時候要在transiton標籤上加上appear,另有三個屬性可指定(appear-?[to|active]?-class)
<!-- 每種CSS動畫庫對應的class命名規則可能不同,所以根據不同庫要自己寫,以animate.css為例 -->
<transition
    name="custom-classes-transition"
    enter-active-class="animated tada"
    leave-active-class="animated bounceOutRight"
    :duration="{ enter: 500, leave: 800 }"
>...</transition>
<!-- duration屬性可以傳一個物件,定製進入和移出的持續時間-->
複製程式碼
  • JS過渡:
  1. 因為現在很多動畫庫需要工程師呼叫庫提供的函式,把dom元素傳入進行處理,這個時候需要這種方式
  2. 通過在transiton這個標籤上新增監聽事件,共8個([before|after]?-?[enter|leave]-?[cancelled]?)
  3. 監聽事件的回撥函式的第一個引數都是el,為過渡的dom元素,在enterleave這兩個還會傳入done作為第二個引數
  4. 元素首次渲染的動畫,可以指定的監聽事件有4個([before|after]?-?appearappear-cancelled)
<template>
    <transition v-bind:css="false"
    v-on:before-enter="beforeEnter" v-on:enter="enter"
    v-on:leave="leave" v-on:leave-cancelled="leaveCancelled">
        <!-- 對於僅使用 JavaScript 過渡的元素新增 v-bind:css="false",Vue 會跳過 CSS 的檢測 -->
    </transition>
</template>
<script>
methods: { // 以Velocity庫為例
    beforeEnter: function (el) {/*...*/},
  // 此回撥函式是可選項的設定
  enter: function (el, done) {
    // Velocity(el, { opacity: 1, fontSize: '1.4em' }, { duration: 300 })
    done()  //回撥函式 done 是必須的。否則,它們會被同步呼叫。
  },
  leave: function (el, done) {
    // Velocity(el, { translateX: '15px', rotateZ: '50deg' }, { duration: 600 })
    done()
  },
  leaveCancelled: function (el) {/*...*/}
}
</script>
複製程式碼

多元素過渡其實就是一句話:照常使用v-if/v-else的同時對同一種標籤加上key來標識

Vue對於這種多元素動畫有佇列上的處理,這就是transiton這個標籤上的mode屬性,通過指定(in-out|out-in)模式,實現消失和出現動畫的佇列效果,讓動畫更加自然。

<transition name="fade" mode="out-in">
  <!-- ... the buttons ... -->
</transition>
複製程式碼

多元件過渡也是一句話:用上一節提到的動態元件,即可完成。

針對列表過渡,其本質仍是多個元素的同時過渡,不過列表大部分是通過陣列動態渲染的,因此有獨特的地方,不過整體的動畫思路不變。具體有以下幾點

  1. 使用transitoin-group這個元件,其需要渲染為一個真實元素,可以通過tag這個屬性來指定。
  2. 列表的每個元素需要提供key屬性
  3. 使用CSS過渡的話,要考慮到列表內容變化過程中,存在相關元素的定位改變,如果要讓定位是平滑過渡的動畫,要另外一個v-move屬性。 這個屬性是通過設定一個css類的樣式,來將建立元素在定位變化時的過渡,Vue內部是通過FLIP實現了一個動畫佇列,只要注意一點就是過渡元素不能設定為display:inline,這裡需要文件上的程式碼做一個簡短的demo:(其實通過在li上設定過渡transition屬性也可以實現v-move的效果)
<template>
    <button v-on:click="shuffle">Shuffle</button>
    <transition-group name="flip-list" tag="ul">
        <li v-for="item in items" v-bind:key="item">{{ item }}</li>
    </transition-group>
</template>
<script>
import _ from 'lodash';
export default {
    data() {
        return {
            items: [1,2,3,4,5,6,7,8,9]
        }
    },
    methods: {
        shuffle: function () {
            this.items = _.shuffle(this.items)
        }
    }
}
</script>
<style lang="css">
.flip-list-move {
  transition: transform 1s;
}
</style>
複製程式碼

數值和屬性動態變化

這一部分的動畫主要是針對資料元素本身的特效,比如數字的增減,顏色的過渡過程控制,svg動畫的實現等,其本質都是數字/文字的變化。 我自己總結就是:通過利用Vue的響應式系統,把數字的變化通過外部庫把DOM上對應數值的變化做出連續的效果,如1->100是個數字遞增的連續過程,黑色->紅色的過程。官方文件主要是用幾個示例程式碼來說明,其本質步驟如下:

  1. 在頁面上通過input的雙向繫結修改某一變數a,還有一個處理dom上的過渡效果的變數b
  2. 這個資料被watcher繫結(watch物件中某個屬性是這個變數a),觸發邏輯
  3. 在watcher裡面的邏輯就是通過外部過渡庫,指定初始值b和最終值a,是把b的值最後改為a
  4. DOM上繫結的變數就是b,如果某些複雜情況可能是基於b的計算屬性,從而把b的變化過程展現出來

  上面這個思路走一遍下來就完成了一個單元級別的動畫效果,這種類似的流程其實是很常見的需求,所以有必要把這個過程封裝成一個元件,只暴露要過渡的值作為入口,每次改變這個值都是一個動畫過渡效果。元件封裝需要在上面四個步驟的基礎上新增mounted生命週期規定初始值即可,同時原來的兩個值a/b在元件裡面作為一個值,可以用watch物件中的newValueoldValue作為區分。   至於最後的SVG,其本質也是數字的過渡,只不過裡面涉及的狀態變數更多,程式碼更長而已,不過純前端頁面這種需求倒還是不多的,不過作為愛好倒可以鼓搗一些好玩的小demo,不過肯定需要設計師的參與,要不那些引數可不好調出來吶。

相關文章