<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          全面了解 Vue.js 函數(shù)式組件

          共 12697字,需瀏覽 26分鐘

           ·

          2021-10-27 22:19

          如果你是一位前端開發(fā)者,又在某些機會下閱讀過一些 Java 代碼,可能會在后者中看到一種類似 ES6 語法中箭頭函數(shù)的寫法

          (String?a,?String?b)?->?a.toLowerCase()?+?b.toLowerCase();

          這種從 Java 8 后出現(xiàn)的 lambda 表達式,在 C++ / Python 中都有出現(xiàn),它比傳統(tǒng)的 OOP 風(fēng)格代碼更緊湊;雖然 Java 中的這種表達式本質(zhì)上還是一個生成類實例的函數(shù)式接口(functional interface)語法糖,但無論其簡潔的寫法,還是處理不可變值并映射成另一個值的行為,都是典型的函數(shù)式編程(FP - functional programming)特征。

          1992 年的圖靈獎得主 Butler Lampson 有一個著名的論斷:

          All problems in computer science can be solved by another level of indirection
          計算機科學(xué)中的任何問題都可以通過增加一個間接層次來解決

          這句話中的“間接層次”常被翻譯成“抽象層”,盡管有人曾爭論過其嚴謹性,但不管怎么翻譯都還說得通。無論如何,OOP 語言擁抱 FP,都是編程領(lǐng)域日益融合并重視函數(shù)式編程的直接體現(xiàn),也印證了通過引入另一個間接層次來解決實際問題的這句“軟件工程基本定理”。

          還有另一句同樣未必那么嚴謹?shù)牧餍姓f辭是:

          OOP 是對數(shù)據(jù)的抽象,而 FP 用來抽象行為

          不同于面向?qū)ο缶幊讨?,通過抽象出各種對象并注重其間的解耦問題等;函數(shù)式編程聚焦于最小的單項操作,將復(fù)雜任務(wù)變成一次次 f(x) = y 式的函數(shù)運算疊加。函數(shù)是 FP 中的一等公民(First-class object),可以被當成函數(shù)參數(shù)或被函數(shù)返回。

          同時在 FP 中,函數(shù)應(yīng)該不依賴或影響外部狀態(tài),這意味著對于給定的輸入,將產(chǎn)生相同的輸出 -- 這也就是 FP 中常常使用“不可變(immutable)”、“純函數(shù)(pure)”等詞語的緣由;如果再把前面提過的 “l(fā)ambda 演算”,以及 “curring 柯里化” 等掛在嘴邊,你聽上去就是個 FP 愛好者了。

          以上這些概念及其相關(guān)的理論,集中誕生在 20 世紀前半葉,眾多科學(xué)家對數(shù)理邏輯的研究收獲了豐碩的成果;甚至現(xiàn)在熱門的 ML、AI 等都受益于這些成果。比如當時大師級的美國波蘭裔數(shù)學(xué)家 Haskell Curry,他的名字就毫不浪費地留在了 Haskell 語言和柯里化這些典型的函數(shù)式實踐中。

          React 函數(shù)式組件

          如果使用過 jQuery / RxJS 時的“鏈式語法”,其實就可以算做 FP 中 monad 的實踐;而近年來大多數(shù)前端開發(fā)者真正接觸到 FP,一是從 ES6 中引入的 map / reduce 等幾個函數(shù)式風(fēng)格的 Array 實例方法,另一個就是從 React 中的函數(shù)式組件(FC - functional component)開始的。

          React 中的函數(shù)式組件也常被叫做無狀態(tài)組件(Stateless Component),更直觀的叫法則是渲染函數(shù)(render function),因為寫出來真的就是個用來渲染的函數(shù)而已:

          const?Welcome?=?(props)?=>?{?
          ??return?<h1>Hello,?{props.name}h1>;?
          }

          結(jié)合 TypeScript 的話,還可以使用 type 和 FC 來對這個返回了 jsx 的函數(shù)約束入?yún)ⅲ?/p>

          type?GreetingProps?=?{
          ?name:?string;
          }

          const?Greeting:React.FC?=?({?name?})?=>?{
          ?return?<h1>Hello?{name}h1>
          };

          也可以用 interface 和范型,更靈活地定義 props 類型:

          interface?IGreeting'm'?|?'f'>?{
          ?name:?string;
          ?gender:?T
          }
          export?const?Greeting?=?({?name,?gender?}:?IGreeting<0?|?1>):?JSX.Element?=>?{
          ?return?<h1>Hello?{?gender?===?0???'Ms.'?:?'Mr.'?}?{name}h1>
          };

          Vue(2.x) 中的函數(shù)式組件

          在 Vue 官網(wǎng)文檔的【函數(shù)式組件】章節(jié)中,這樣描述到:

          ...我們可以將組件標記為 functional,這意味它無狀態(tài)?(沒有響應(yīng)式數(shù)據(jù)),也沒有實例?(沒有 this 上下文)。一個函數(shù)式組件就像這樣:
          ?
          Vue.component('my-component',?{
          ??functional:?true,
          ??//?Props?是可選的
          ??props:?{
          ????//?...
          ??},
          ??//?為了彌補缺少的實例
          ??//?提供第二個參數(shù)作為上下文
          ??render:?function?(createElement,?context)?{
          ????//?...
          ??}
          })

          ...
          ?
          在 2.5.0?及以上版本中,如果你使用了[單文件組件],那么基于模板的函數(shù)式組件可以這樣聲明:

          <template?functional>
          template>

          寫過 React 并第一次閱讀到這個文檔的開發(fā)者,可能會下意識地發(fā)出 “啊這...” 的感嘆,寫上個 functional 就叫函數(shù)式了???

          實際上在 Vue 3.x 中,你還真的能和 React 一樣寫出那種純渲染函數(shù)的“函數(shù)式組件”,這個我們后面再說。

          在目前更通用的 Vue 2.x 中,正如文檔中所說,一個函數(shù)式組件(FC - functional component)就意味著一個沒有實例(沒有 this 上下文、沒有生命周期方法、不監(jiān)聽任何屬性、不管理任何狀態(tài))的組件。從外部看,它大抵也是可以被視作一個只接受一些 prop 并按預(yù)期返回某種渲染結(jié)果的 fc(props) => VNode 函數(shù)的。

          并且,真正的 FP 函數(shù)基于不可變狀態(tài)(immutable state),而 Vue 中的“函數(shù)式”組件也沒有這么理想化 -- 后者基于可變數(shù)據(jù),相比普通組件只是沒有實例概念而已。但其優(yōu)點仍然很明顯:

          • 因為函數(shù)式組件忽略了生命周期和監(jiān)聽等實現(xiàn)邏輯,所以渲染開銷很低、執(zhí)行速度快
          • 相比于普通組件中的 v-if 等指令,使用 h 函數(shù)或結(jié)合 jsx 邏輯更清晰
          • 更容易地實現(xiàn)高階組件(HOC - higher-order component)模式,即一個封裝了某些邏輯并條件性地渲染參數(shù)子組件的容器組件
          • 可以通過數(shù)組返回多個根節(jié)點

          ?? 舉個栗子:優(yōu)化 el-table 中的自定義列

          先來直觀感受一個適用 FC 的典型場景:

          這是 ElementUI 官網(wǎng)中對自定義表格列給出的例子,其對應(yīng)的 template 部分代碼為:

          <template>
          ??<el-table
          ????:data="tableData"
          ????style="width:?100%">

          ????<el-table-column
          ??????label="日期"
          ??????width="180">

          ??????<template?slot-scope="scope">
          ????????<i?class="el-icon-time">i>
          ????????<span?style="margin-left:?10px">{{?scope.row.date?}}span>
          ??????template>
          ????el-table-column>
          ????<el-table-column
          ??????label="姓名"
          ??????width="180">

          ??????<template?slot-scope="scope">
          ????????<el-popover?trigger="hover"?placement="top">
          ??????????<p>姓名:?{{?scope.row.name?}}p>
          ??????????<p>住址:?{{?scope.row.address?}}p>
          ??????????<div?slot="reference"?class="name-wrapper">
          ????????????<el-tag?size="medium">{{?scope.row.name?}}el-tag>
          ??????????div>
          ????????el-popover>
          ??????template>
          ????el-table-column>
          ????<el-table-column?label="操作">
          ??????<template?slot-scope="scope">
          ????????<el-button
          ??????????size="mini"
          ??????????@click="handleEdit(scope.$index,?scope.row)">
          編輯el-button>
          ????????<el-button
          ??????????size="mini"
          ??????????type="danger"
          ??????????@click="handleDelete(scope.$index,?scope.row)">
          刪除el-button>
          ??????template>
          ????el-table-column>
          ??el-table>
          template>

          在實際業(yè)務(wù)需求中,像文檔示例中這種小表格當然存在,但并不會成為我們關(guān)注的重點;ElementUI 自定義表格列被廣泛地用于各種字段繁多、交互龐雜的大型報表的渲染邏輯中,通常是 20 個以上的列起步,并且每個列中圖片列表、視頻預(yù)覽彈窗、需要組合和格式化的段落、根據(jù)權(quán)限或狀態(tài)而數(shù)量不定的操作按鈕等等,不一而足;相關(guān)的 template 部分也經(jīng)常是幾百行甚至更多,除了冗長,不同列直接相似的邏輯難以復(fù)用也是個問題。

          正如電視劇《老友記》中臺詞所言:

          歡迎來到現(xiàn)實世界!它糟糕得要命~ 但你會愛上它!

          vue 單文件組件中并未提供 include 等拆分 template 的方案 -- 畢竟語法糖可夠多了,沒有最好。

          有潔癖的開發(fā)者會嘗試將復(fù)雜的列模版部分封裝成獨立的組件,來解決這個痛點;這樣已經(jīng)很好了,但相比于本來的寫法又產(chǎn)生了性能隱患。

          回想起你在面試時,回答關(guān)于如何優(yōu)化多層節(jié)點渲染問題時那種氣吞萬里的自信??,我們顯然在應(yīng)該在這次的實踐中更進一步,既能拆分關(guān)注點,又要避免性能問題,函數(shù)式組件就是一種這個場景下合適的方案。

          首先嘗試的是把原本 template 中日期列的部分“平移”到一個函數(shù)式組件 DateCol.vue 中:

          <template?functional>
          ??<div>
          ????<i?class="el-icon-time">i>
          ????<span?style="margin-left:?10px;?color:?blue;">{{?props.row.date?}}span>
          ??div>
          template>

          在容器頁面中 import 后聲明在 components 中并使用:

          基本是原汁原味;唯一的問題是受限于單個根元素的限制,多套了一層 div,這一點上也可以用 vue-fragment 等加以解決。

          接下來我們將姓名列重構(gòu)為 NameCol.js:

          export?default?{
          ??functional:?true,
          ??render(h,?{props})?{
          ????const?{row}?=?props;
          ????return?h('el-popover',?{
          ????????props:?{trigger:?"hover",?placement:?"top"},
          ????????scopedSlots:?{
          ??????????reference:?()?=>?h('div',?{class:?"name-wrapper"},?[
          ????????????h('el-tag',?{props:?{size:?'medium'}},?[row.name?+?'~'])
          ??????????])
          ????????}
          ??????},?[
          ??????????h('p',?null,?[`姓名:?${?row.name?}`]),
          ??????????h('p',?null,?[`住址:?${?row.address?}`])
          ??????])
          ??}
          }


          效果沒得說,還用數(shù)組規(guī)避了單個根元素的限制;更重要的是,抽象出來的這個小組件是真正的 js 模塊,你可以不用

          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                    <th id="afajh"><progress id="afajh"></progress></th>
                    男人的天堂欧美 | 日本特级黄色电影免费看 | 操逼视频毛片 | 91欧美性爱 | 成人精品视频网站 |