高級 Vue 組件模式 (9)

09 使用 Functional 組件

目標

到此爲止,咱們的 toggle 組件已經足夠強大以及好用了,所以這篇文章不會再爲它增長新的特性。若是你是從第一篇文章一直讀到這裏的讀者,你必定會發現,整篇文章中,我幾乎沒有對 toggle-ontoggle-off 作出任何更改和重構,所以這篇文章着重來重構一下它們。vue

以前一直沒有對它們進行任何更改,一個很重要的緣由是由於它們足夠簡單,組件內部沒有任何 data 狀態,僅靠外部傳入的 prop 屬性 on 來決定內部渲染邏輯。這聽起來彷佛有些耳熟啊,沒錯,它們就是在上一篇文章中所說起的木偶組件(Dump Component)。在 Vue 中,這種類型的組件也能夠叫作函數式組件(Functional Component)。git

仔細觀察 app 組件的模板代碼,會發現存在必定的冗餘性的,好比:github

<toggle-on :on="status.on">{{firstTimes}}</toggle-on>
<toggle-off :on="status.on">{{firstTimes}}</toggle-off>

這裏兩行代碼的邏輯幾乎如出一轍,但咱們卻要寫兩次。同時你還會發現一個問題,因爲其內部的渲染邏輯是經過 v-if 來描述的,實際上在 Vue 渲染完成後,會渲染兩個 dom 節點,在切換時的狀態從 devtool 中觀察的效果大概是這樣子的:app

clipboard.png

未顯示的節點是一個註釋節點,而顯示的節點是一個 div 節點。dom

這篇文章將着重解決這兩個問題:函數

  • toggle-ontoggle-off 合二爲一,減小代碼冗餘性
  • 重構以 v-if 實現的渲染邏輯,改成更好的動態渲染邏輯(僅使用一個 dom 節點)

實現

轉化爲函數式組件

首先,先將已經存在的 toggle-ontoggle-off 組件轉化爲函數式組件,很簡單,只需保留 template 代碼塊便可,同時在左邊的標籤上聲明 functional 屬性,或者在 script 代碼塊中聲明 functional: true 也是能夠的。惟一要注意的是,因爲函數式組件沒有 data 也沒有 this,所以全部模板中用到的與 prop 相關的渲染邏輯,都要做出相應更改,好比原先的 on 要改成 props.on的形式,因爲這裏咱們要移除 v-if 的渲染邏輯,所以直接移除便可,詳細代碼以下:測試

// ToggleOn.vue
<template functional>
  <div class="toggle-on"><slot></slot></div>
</template>

<style>
.toggle-on {
  color: #86d993;
}
</style>

// ToggleOff.vue
<template functional>
  <div class="toggle-off"><slot></slot></div>
</template>

<style>
.toggle-off {
  color: red;
}
</style>

除此以外,還能夠發現,我爲兩個組件增長不一樣的顏色樣式以便於區分當前的開關狀態。this

實現 ToggleStatus 組件

接下來實現今天的主角,ToggleStatus 組件,因爲咱們的目標是將原先的二個函數式組件合二爲一,所以這個組件自己應當也是一個函數式組件,不過咱們須要使用另一種寫法,以下:spa

<script>
import ToggleOn from './ToggleOn'
import ToggleOff from './ToggleOff'

export default {
  functional: true,
  render(createElement, {props, data, children}) {
    let Comp = ToggleOff
    
    if(props.on) Comp = ToggleOn

    return createElement(Comp, data, children)
  }
}
</script>

關於這種寫法中,rendercreateElement 方法的參數就不贅述了,不熟悉的讀者請參考官方文檔。能夠發現,這裏將 toggle-ontoggle-off 以模塊的形式導入,以後由 props.on 的值進行斷定,從而決定哪個被做爲 createElement 方法的第一個參數進行渲染。3d

諸如 datachildren 參數咱們原封不動的傳入 createElement 便可,由於這裏對於 toggle-status 組件的定位是一個代理組件,對於其餘參數以及子元素應當原封不動的傳遞給被代理的組件中。

以後在 app 組件中更改響應的渲染邏輯便可,以下:

// controlled toggle
<toggle-status :on="status.on">{{firstTimes}}</toggle-status>

// uncontrolled toggle
<toggle-status :on="status.on">{{secondTimes}}</toggle-status>

成果

一切如原先同樣,只不過此次咱們能夠少寫一行冗餘的代碼了。同時打開 devtool 能夠發現,兩種狀態的組件會複用同一個 dom 節點,以下:

clipboard.png

你能夠經過下面的連接來看看這個組件的實現代碼以及演示:

總結

關於函數式組件,我是在 React 中第一次接觸,其形式和它的名字同樣,就是一個函數。這種組件和普通組件相比的優點主要在於,它是無狀態的,這意味着它的可測試性和可讀性更好,同時一些狀況下,渲染開銷也更小。

咱們在平常工做中,可能會常常遇到動態渲染的需求,通常狀況下,咱們均會經過 v-if 來解決,在比較簡單的狀況下,v-if 確實一種很簡單且高效的方式,可是隨着組件複雜度的上升,極可能會出現麪條式的代碼,可讀性和可測試性都大打折扣,這是不妨換一個角度從渲染機制自己將組件重構爲更小的顆粒,並用一個函數式組件動態的代理它們,可能會獲得更好的效果,舉一個比較常見的例子,好比表單系統中的表單項,通常都具備多種渲染狀態,如編輯狀態、瀏覽狀態、禁用狀態等等,這時利用該模式來抽離不一樣狀態的渲染邏輯就很是不錯。

目錄

github gist

關注公衆號 全棧101,只談技術,不談人生

clipboard.png

相關文章
相關標籤/搜索