譯者:zhangolve
鏈接:http://www.zcfy.cc/article/1980
原文:https://medium.com/@housecor/react-stateless-functional-components-nine-wins-you-might-have-overlooked-997b0d933dbc#.7lv7ick6a
React 0.14 引進(jìn)了一種更簡(jiǎn)單的方式來(lái)定義組件,也就是無(wú)狀態(tài)組件。這種組件使用了原生Javascript 函數(shù),下圖就是在用ES6的情況下婴梧,React 0.14 之前版本的組件寫(xiě)法和它之后對(duì)應(yīng)的組件寫(xiě)法橙弱。
前者用了27行代碼來(lái)實(shí)現(xiàn)谣殊,而后者只用了21行建椰,這當(dāng)然也沒(méi)有什么了不起的搪柑。需要說(shuō)明的是世分,由于性能方面的原因编振,右邊代碼中的sayHi
函數(shù)應(yīng)該盡量避免這樣使用。(原因見(jiàn)文末評(píng)論)
匆匆一覽,上面左右兩種代碼形式似乎并沒(méi)有太大區(qū)別踪央,但是能夠把噪音去掉就是偉大的勝利臀玄。(譯者注:本文中所說(shuō)的噪音(noise)和信號(hào)(signal)是來(lái)自于物理學(xué)中的概念。下文中其他處遇到這兩個(gè)詞畅蹂,也是如此健无,不再解釋。)
下面就是原因了液斜。
Class并無(wú)必要
誠(chéng)然累贤,我認(rèn)為圍繞著ES6 的類(lèi)的討論已經(jīng)太多了。但是少漆,我仍然認(rèn)為純函數(shù)是更加合理的臼膏,把討厭的結(jié)構(gòu)體和繼承關(guān)系去掉無(wú)疑是件好事,體現(xiàn)了它的優(yōu)勢(shì)示损。
沒(méi)有this關(guān)鍵字
就如上面的例子所示渗磅,無(wú)狀態(tài)組件只是一個(gè)函數(shù)。因此检访,所有與Javascript語(yǔ)言中this 關(guān)鍵字有關(guān)的讓人疑惑始鱼,使人討厭的情況都會(huì)避免。沒(méi)有了this 關(guān)鍵字之后脆贵,整個(gè)組件就會(huì)更加容易讓人理解医清。只需要比較一下上例中的click 事件處理器,就能明白了卖氨。
onClick={this.sayHi.bind(this)}>Say Hi</a>
onClick={sayHi}>Say Hi</a>
需要說(shuō)明的是会烙,對(duì)于無(wú)狀態(tài)組件而言,bind這個(gè)關(guān)鍵字并非必須的双泪。將Class 去掉之后,我們也無(wú)需通過(guò)綁定來(lái)將this關(guān)鍵字與上下文聯(lián)系起來(lái)密似。 Javascript 中的 this 關(guān)鍵字給如此多的開(kāi)發(fā)者造成了困惑焙矛,能夠盡量不用this 是無(wú)狀態(tài)組件的另一個(gè)優(yōu)勢(shì)。
被迫使用最好的方法
無(wú)狀態(tài)組件是非常有用的表現(xiàn)類(lèi)組件残腌。表現(xiàn)類(lèi)組件應(yīng)該專(zhuān)注于UI層面而不是交互村斟,應(yīng)該避免在表現(xiàn)類(lèi)組件中使用狀態(tài)。狀態(tài)應(yīng)當(dāng)通過(guò)更高水平的容器類(lèi)組件抛猫,或者是通過(guò)FLUX/Redux 等等來(lái)管理蟆盹。無(wú)狀態(tài)組件不支持狀態(tài)或者生命周期。這是一件好事闺金。為什么這么說(shuō)呢逾滥?因?yàn)樗仁鼓悴辉賾卸瑁屇悴坏貌话讶萜黝?lèi)組件和表現(xiàn)類(lèi)組件分離開(kāi)來(lái)败匹。
當(dāng)然寨昙,你總是很愿意將狀態(tài)添加到表現(xiàn)類(lèi)組件之中讥巡,這也的確是一種很快的方法來(lái)實(shí)現(xiàn)一個(gè)功能。然而舔哪,無(wú)狀態(tài)組件并不支持本地狀態(tài)欢顷,你并不能夠輕而易舉地實(shí)現(xiàn)狀態(tài)。因此捉蚤,無(wú)狀態(tài)組件就自動(dòng)迫使當(dāng)前組件保持純函數(shù)形式抬驴。你也要被迫把狀態(tài)管理放到它相應(yīng)的位置:在更高水平的容器類(lèi)組件中。
高信噪比
正如上圖所示缆巧,無(wú)狀態(tài)組件寫(xiě)起來(lái)代碼量更少布持,這也就減少了噪音干擾。正如我在“Writing Code for Humans 課程” 中所討論過(guò)的那樣盅蝗,好代碼把信噪比(signal-to-noise ratio)最大化鳖链,27行代碼的組件變成了21行,縮減了大約20%的代碼量墩莫。如果你使用ES6 結(jié)構(gòu)來(lái)處理屬性(props)芙委,那么結(jié)果是幾乎所有的信號(hào)(signal)都是有用的。
import React from ‘react’;
const HelloWorld = ({name}) => (
<div>{`Hi ${name}`}</div>
);
export default HelloWorld;
看見(jiàn)了吧狂秦,只用了一個(gè)函數(shù)灌侣,有一個(gè)參數(shù),返回了一個(gè)標(biāo)記(markup)裂问。是不是很Nice 侧啼!那么,還能不能讓代碼量更少呢堪簿?
代碼自動(dòng)補(bǔ)全
如果你就像是我上面例子中那樣痊乾,用ES6 拆解你的屬性(props) ,那么你現(xiàn)在用到的所有的數(shù)據(jù)都只是作為一個(gè)簡(jiǎn)單的函數(shù)參數(shù)而已椭更。這也就意味著哪审,相較于基于類(lèi)(class-based)的組件,你也得到了很好的自動(dòng)補(bǔ)全代碼支持。
很容易找出來(lái)臃腫的組件和低劣的數(shù)據(jù)結(jié)構(gòu)
眾所周知虑瀑,如果一個(gè)函數(shù)有很多參數(shù)湿滓,那它就是有代碼異味的 。當(dāng)你使用ES6 架構(gòu)來(lái)組織你的無(wú)狀態(tài)組件時(shí)舌狗,這個(gè)參數(shù)列表已經(jīng)清晰地傳遞了你組件的依賴(lài)關(guān)系叽奥。因此,也就很容易找到需要更改的組件痛侍。在上面的例子中朝氓,你也可以打破這個(gè)組件結(jié)構(gòu)或者重新思考你正傳遞的數(shù)據(jù)結(jié)構(gòu)。有時(shí)候,你并不需要傳遞一長(zhǎng)串屬性膀篮,而只需要傳遞一個(gè)對(duì)象嘹狞。但是如果這些屬性不僅僅與單獨(dú)的一個(gè)對(duì)象相匹配,那么你就需要重構(gòu)你的組件誓竿,把這個(gè)組件化整為零磅网,分成若干個(gè)單獨(dú)的組件。
便于理解
正如我們所見(jiàn)筷屡,當(dāng)你看到一個(gè)無(wú)狀態(tài)組件的時(shí)候涧偷,你就知道這其實(shí)就是一個(gè)簡(jiǎn)單的函數(shù),它有相應(yīng)的屬性毙死,可以生成HTML燎潮。即便在它的render 函數(shù)內(nèi)部嵌套了很多其他的函數(shù),它在概念上仍然是很簡(jiǎn)單的扼倘。這又是無(wú)狀態(tài)組件的一個(gè)很大的優(yōu)勢(shì)所在确封。
便于測(cè)試
因?yàn)闊o(wú)狀態(tài)組件只是一個(gè)純函數(shù),你的實(shí)現(xiàn)是非常直接的再菊。給這些值對(duì)應(yīng)的屬性(props),我期望它返回這個(gè)標(biāo)記(markup)爪喘。因此對(duì)于上面的示例 HelloWorld 組件而言,我可以把'Cory' 這個(gè)值傳遞給 props.name 纠拔,這樣這個(gè)組件就可以返回一個(gè)內(nèi)部含有'Hi Cory' 的 div 了秉剑。
由于有了React 的無(wú)狀態(tài)組件,每一個(gè)組件都可以單獨(dú)進(jìn)行測(cè)試稠诲。無(wú)需模擬侦鹏,也無(wú)需狀態(tài)的操作,或者安裝用于測(cè)試的特定的庫(kù)臀叙。
表現(xiàn)
最后略水,無(wú)狀態(tài)組件會(huì)在不久之后的將來(lái)提供更加優(yōu)秀的性能表現(xiàn)。由于針對(duì)無(wú)狀態(tài)組件劝萤,無(wú)需考慮狀態(tài)或者生命周期渊涝,React 團(tuán)隊(duì)也計(jì)劃在將來(lái)的版本中去掉不必要的檢查和內(nèi)存分配。而伴隨更加優(yōu)秀的性能表現(xiàn)而來(lái)的稳其,是更好的語(yǔ)法規(guī)則驶赏,代碼更加可讀化炸卑,代碼更加易于測(cè)試既鞠。怎么樣,這么好的東西盖文,趕緊用起來(lái)吧嘱蛋!
Summary
基于以上這些原因,我們應(yīng)該在可用之處盡量使用無(wú)狀態(tài)組件。這也讓我熱愛(ài)教授React 課程 有了一個(gè)新的原因洒敏。在當(dāng)今流行的框架中龄恋,React 的無(wú)狀態(tài)組件是我見(jiàn)過(guò)的最優(yōu)雅的實(shí)現(xiàn)可復(fù)用組件的方式,沒(méi)有之一凶伙。 是的郭毕,甚至包括 Angular 2在內(nèi),通通不在話(huà)下函荣。
你們發(fā)現(xiàn)我有什么疏漏的地方显押?可以在下面評(píng)論框里留言哦!
注:
來(lái)自原文下評(píng)論:
Felipe Amorim :如果在render 方法中定義你的函數(shù)的話(huà)傻挂,你不必再考慮綁定了乘碑。但是 由于無(wú)狀態(tài)組件并不提供 shouldComponentUpdate 方法,當(dāng)創(chuàng)建新的函數(shù)的時(shí)候金拒,你也就不能阻止不必要的重新渲染了兽肤。這也就造成了性能上的問(wèn)題。
Satyajit Sahoo: 應(yīng)該盡量避免在無(wú)狀態(tài)組件內(nèi)部使用其他函數(shù)绪抛,完全可以將這些函數(shù)放到無(wú)狀態(tài)組件之外资铡,通過(guò)傳遞屬性值的方式來(lái)實(shí)現(xiàn),這將給性能帶來(lái)很大提升睦疫。