Vue renderless組件(函數(shù)式組件)

開門見山叉钥,我在codepen上寫了個簡單demo:上下兩個按鈕罢缸,功能一致(開關(guān));不同之處是:前者只改變文字投队,后者順帶改變了背景顏色枫疆。OK,怎么實現(xiàn)呢敷鸦?

Two Toggles

實現(xiàn)相同功能的模塊息楔,理想方式自然是提供通用的抽象組件。這個例子中扒披,想要配合背景更換的效果值依,最直觀的設(shè)計應(yīng)該是實現(xiàn)一個子組件,再根據(jù)父組件傳遞的props值——比如一個叫backgroud的function——聯(lián)動更改背景色碟案。
不過鳞滨,在某些場景中,我們想要的是模塊提供固定的功能邏輯蟆淀,但并不希望它限制頁面的渲染(比如拯啦,我希望給開關(guān)加上更酷炫的動態(tài)效果,而不僅僅只有更換背景色這種單調(diào)的操作)熔任。組件設(shè)計者顯然不可能盡善盡美地提供所有候選props褒链,這時候留出更多的自定義空間反倒是一個比較切合實際的解決途徑。

Render函數(shù)

進(jìn)入正題前疑苔,先簡介一下Vue是如何渲染組件的甫匹。眾所周知,在工程中,我們會在.vue文件中定義<template>兵迅、<script><style>三種tag抢韭,分別盛放組件html、javascript和css恍箭。

<template>
  <button class="mood">
    {{ state ? 'On' : 'Off' }}
  </button>
</template>

<script>
export default {
  data: () => ({ state: false })
}
</script>

<style>
.mood:after {
  color: white;
  background: blue;
}
</style>

但事實上刻恭,最后在生產(chǎn)環(huán)境中,我們只使用了一個巨大的JS文件——just JavaScript扯夭。究其緣由還是得益于webpack的vue loader鳍贾,它幫助我們把上述三部分提取出來,比如上述的.vue文件交洗,經(jīng)過vue loader后骑科,大體會成為如下這種樣式:

exprot default {
  template: `<button class="mood">{{ state ? 'On' : 'Off' }}</button>`,
  data: ()  => ({ state: false })
}

vuejs會把template元素提取出來,并進(jìn)一步編譯成一個叫render的函數(shù)构拳。(有關(guān)render函數(shù)可以參考官方文檔

render(h) {
  return h(
    'button',
    {class: 'mood'},
    state ? 'On' : 'Off'
  )
}

render函數(shù)最后會被vue優(yōu)化成VNode(虛節(jié)點)咆爽,具體過程我不再贅述了。不過置森,這里提供了一個很有趣的思路:編寫組件時伍掀,我們其實可以不寫vue文件,不寫template暇藏,只需要寫render函數(shù)蜜笤。

const button = {
  render(h) {
    return h(
      'button',
      {class: 'mood'},
      state ? 'On' : 'Off'
    )
  },
  data() {
    return {state: false}
  }
}

So? Renderless?

前提概要結(jié)束了,這里引入一個Renderless component的概念盐碱,直譯的話應(yīng)該叫非渲染組件把兔,國內(nèi)好多人喜歡叫它函數(shù)式組件。

Renderless意思就是組件只提供數(shù)據(jù)操作瓮顽,不渲染任何內(nèi)容县好。我們擱置爭議,只看非渲染組件的具體實現(xiàn)暖混。

const toggle = {
    render() {
        ...
    },
    data() {
        return { state: true }
    },
    methods: {
        toggle() {
            this.state = !this.state
        }
    }
}

new Vue({
    el: '#parent',
    components: { toggle },
    ...
})

toggle就是所謂的Renderless組件了缕贡,只有數(shù)據(jù)和方法,不提供html template拣播。父組件直接將其放入components即可當(dāng)作一般子組件使用晾咪。

Slots in Renderless

那誰負(fù)責(zé)渲染工作呢?嗯贮配,就是Slots谍倦!父組件通過傳遞自定義的slots來定制子組件的html template。

<toggle v-slot:default="{on, toggle}">
    <div class="container">
        <button @click="click(toggle)">
            {{on ? 'On' : 'Off'}}
        </button>
    </div>
</toggle>

這里提一下v-slot泪勒,它是vue 2.6以后的新語法昼蛀,用來代替之前的slotslot-scope宴猾;v-slot:default還可以簡寫成#default。Vue3應(yīng)該不會再保留slotslot-scope這種不倫不類的標(biāo)簽了叼旋。

Scoped Slots

<toggle #default="{on, toggle}">

上文中用到了作用域插槽仇哆。這個例子中我希望能讓插槽訪問到子組件toggle里的數(shù)據(jù)和方法,以便之后點擊button更改狀態(tài)夫植。子組件暴露作用域插槽也很簡單讹剔,只要在render函數(shù)里返回$scopedSlots對象即可,這里因方便起見使用了默認(rèn)的default插槽偷崩,自己實現(xiàn)的時候也可以重命名為任意插槽辟拷。

//toggle.js
const toggle = {
    render() {
        return this.$scopedSlots.default({
            on: this.state,
            toggle: this.toggle,
        })
    },
    data() {
        return { state: true }
    },
    methods: {
        toggle() {
            this.state = !this.state
        }
    }
}

Using toggle component

最后我們在父組件調(diào)用renderless組件:

<template>
  <toggle v-slot="{on, toggle}">
        <div class="container">
            <button @click="click(toggle)">
                {{on ? 'On' : 'Off'}}
            </button>
        </div>
    </toggle>
</template>

<script>
import toggle from 'toggle';

export default {
  components: { toggle },
    methods: {
        click(fn) {
            fn()
        },
    },
}
</script>

這樣一個簡單的renderless開關(guān)就實現(xiàn)了撞羽,

Simple Toggle

Customized Component

假如你想自定義組件樣式阐斜,或是說控制toggle渲染方式,更改也很容易诀紊,只需要在插槽里寫下自定義代碼即可:

<toggle #default="{ on, toggle  }">
    <div class="container">
        <button @click="click(toggle)"
                :style="{background: on ? 'green' : 'red'}">
            {{on ? 'On' : 'Off'}}
        </button>
    </div>
</toggle>

因為toggle的邏輯不變谒出,所以我們不需要更改這個renderless組件。只需稍微改動一下slot邻奠,button的背景色就會隨著開關(guān)一齊改變了笤喳。嗯,這就是Renderless組件的效果碌宴,功能邏輯和頁面渲染分開杀狡。

Toggle with background

更炫酷的開關(guān)就由大家來完成吧。

小結(jié)

這期用一個很簡單的例子科普了Renderless Component贰镣。所謂Renderless就是利用render函數(shù)和slot呜象,將組件的功能邏輯與前端渲染分離開來,這種設(shè)計更符合傳統(tǒng)軟件工程的單一職責(zé)和開放閉合原則碑隆。當(dāng)然這和VUE設(shè)計之初的理念并不相符恭陡,vue作者似乎并不屑于這種形式,我在尤雨溪的某些文章里還看到他噴renderless嘩眾取寵上煤,帶來無謂的的性能開銷休玩。
我自己倒是挺贊許renderless的。在工程開發(fā)中確實會碰到了功能邏輯相似劫狠,但樣式表現(xiàn)不一的組件簇拴疤;通過將底層邏輯以renderless子組件的形式封裝起來,可以很好地實現(xiàn)代碼復(fù)用的目標(biāo)《琅ⅲ現(xiàn)實開發(fā)中遥赚,具體情況還是要具體分析滴。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末阐肤,一起剝皮案震驚了整個濱河市凫佛,隨后出現(xiàn)的幾起案子讲坎,更是在濱河造成了極大的恐慌,老刑警劉巖愧薛,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件晨炕,死亡現(xiàn)場離奇詭異,居然都是意外死亡毫炉,警方通過查閱死者的電腦和手機(jī)瓮栗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瞄勾,“玉大人费奸,你說我怎么就攤上這事〗福” “怎么了愿阐?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長趾疚。 經(jīng)常有香客問我缨历,道長,這世上最難降的妖魔是什么糙麦? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任辛孵,我火速辦了婚禮,結(jié)果婚禮上赡磅,老公的妹妹穿的比我還像新娘魄缚。我一直安慰自己,他們只是感情好焚廊,可當(dāng)我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布冶匹。 她就那樣靜靜地躺著,像睡著了一般节值。 火紅的嫁衣襯著肌膚如雪徙硅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天搞疗,我揣著相機(jī)與錄音嗓蘑,去河邊找鬼。 笑死匿乃,一個胖子當(dāng)著我的面吹牛桩皿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播幢炸,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼泄隔,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了宛徊?” 一聲冷哼從身側(cè)響起佛嬉,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤逻澳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后暖呕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體斜做,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年湾揽,在試婚紗的時候發(fā)現(xiàn)自己被綠了瓤逼。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡库物,死狀恐怖霸旗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情戚揭,我是刑警寧澤诱告,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站毫目,受9級特大地震影響蔬啡,放射性物質(zhì)發(fā)生泄漏诲侮。R本人自食惡果不足惜镀虐,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望沟绪。 院中可真熱鬧刮便,春花似錦、人聲如沸绽慈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽坝疼。三九已至搜贤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間钝凶,已是汗流浹背仪芒。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留耕陷,地道東北人掂名。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像哟沫,于是被迫代替她去往敵國和親饺蔑。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 這篇筆記主要包含 Vue 2 不同于 Vue 1 或者特有的內(nèi)容嗜诀,還有我對于 Vue 1.0 印象不深的內(nèi)容猾警。關(guān)于...
    云之外閱讀 5,050評論 0 29
  • 組件(Component)是Vue.js最核心的功能孔祸,也是整個架構(gòu)設(shè)計最精彩的地方,當(dāng)然也是最難掌握的发皿。...
    六個周閱讀 5,614評論 0 32
  • 前幾天想學(xué)學(xué)Vue中怎么編寫可復(fù)用的組件融击,提到要對Vue的render函數(shù)有所了解■撸可仔細(xì)一想尊浪,對于Vue的ren...
    kangaroo_v閱讀 116,062評論 13 171
  • Test Vue.js Slots in Jest 測試Vue.js中的Slots插槽 Learn how to ...
    Revontulet閱讀 2,969評論 0 1
  • 一、了解Vue.js 1.1.1 Vue.js是什么封救? 簡單小巧拇涤、漸進(jìn)式、功能強(qiáng)大的技術(shù)棧 1.1.2 為什么學(xué)習(xí)...
    蔡華鵬閱讀 3,324評論 0 3