React那些事

背景

React是=起源于 Facebook 的內(nèi)部項目,因為該公司對市場上所有 JavaScript MVC 都不滿意,就決定自己寫一套,用來架設(shè) Instagram 的網(wǎng)站,2013年5月開源

WHAT

官網(wǎng)是這樣解釋的 —— A JavaScript library for building user interfaces,三大特性如下:

  • Declarative
    聲明式編程,告訴機(jī)器你想要什么(What),讓機(jī)器想出如何做(How),
    對應(yīng)的是命令式編程,命令機(jī)器如何去做某件事(How),而不管你要的是什么,它都會按照你的命令實現(xiàn)。
  • Component-Based
    任何一個功能獨立的模塊都定義成組件,一個個組件通過不斷復(fù)用,組合與嵌套,構(gòu)成一套完整的UI界面。
  • Learn Once, Write Anywhere

核心思想

組件化思想

React 讓我們重新規(guī)劃界面,把任何一個功能獨立的模塊都定義成組件,即被獨立封裝的可復(fù)用 UI 部件。一個個的組件通過不斷復(fù)用,組合與嵌套等,構(gòu)成一套完整的 UI 界面。

一個組件的寫法

import React, { Component } from 'react';
import { render } from 'react-dom';

class HelloMessage extends Component {
  render() {
    return <div>Hello {this.props.name}</div>;
  }
}
render(<HelloMessage name="world" />, mountNode);

render會把這個組件顯示到頁面上的某個元素mountNode里面,顯示的內(nèi)容就是<div>Hello world</div>

組件的劃分

  • 把 UI 劃分出組件層級,可以想想什么情況下我們需要新建一個函數(shù)或?qū)ο?/strong>)

    • 單一功能原則, DRY
    • 一個組件應(yīng)該只做一件事情。如果這個組件功能不斷豐富,它應(yīng)該被分成更小的組件

JSX語法——有些人喜歡它,而其他人認(rèn)為這是一個很大的退步,

不得不說是一種非常聰明的做法,JSX代替?zhèn)鹘y(tǒng)的HTML Templates,讓前端實現(xiàn)真正意義上的組件化成為了可能

HTML直接嵌入了JS代碼里面,前端被“表現(xiàn)和邏輯層分離”這種思想“洗腦”太久了,可能不好接受的設(shè)定之一。

實際上組件的HTML是組成一個組件不可分割的一部分,能夠?qū)TML封裝起來才是組件的完全體,React發(fā)明了JSX讓JS支持嵌入HTML,但它具有無可爭議的優(yōu)點:靜態(tài)分析,JSX標(biāo)記中發(fā)生錯誤,編譯器會立即報錯而不是留待運行時出現(xiàn)莫名其妙的問題。這有助于開發(fā)人員快速排查錯誤以及避免其它愚蠢的錯誤,比如拼寫錯誤。

好處是你可以不一定使用這種語法,但是要使用包含JSX 的組件,是需要“編譯”輸出JS 代碼才能使用的

Virtual DOM

DOM是什么?

DOM是Document Object Model,就是將XML(或者HTML)內(nèi)的節(jié)點定義成基本統(tǒng)一的對象數(shù)據(jù)可以供程序語言(如javaScript)控制的技術(shù)規(guī)范
通過 DOM 你可以改變網(wǎng)頁。

你可以使用 Javascript 語言來操作 DOM 以改變網(wǎng)頁。

為了改變網(wǎng)頁,你必須告訴 Javascript 改變哪一個節(jié)點。這就是操作 DOM。

真正的 DOM 元素非常龐大,這是因為標(biāo)準(zhǔn)就是這么設(shè)計的。而且操作它們的時候你要小心翼翼,輕微的觸碰可能就會導(dǎo)致頁面重排,這可是殺死性能的罪魁禍?zhǔn)住?/p>

React 最大的特色是當(dāng)View層在渲染的時候,它不會直接從模板里面去構(gòu)建一個DOM節(jié)點. 首先, 它創(chuàng)建一些暫時的, 虛擬的 DOM, 然后和真實的DOM還有創(chuàng)建的Diffs一起做對比, 然后才決定需不需要渲染。

當(dāng)組件狀態(tài)state有更改的時候,React會自動調(diào)用組件的render方法重新渲染整個組件的UI,所以React實現(xiàn)了一個Virtual DOM,組件DOM結(jié)構(gòu)就是映射到這個Virtual DOM上,React在這個Virtual DOM上實現(xiàn)了一個diff算法,當(dāng)要重新渲染組件的時候,會通過diff尋找到要變更的DOM節(jié)點,再把這個修改更新到瀏覽器實際的DOM節(jié)點上,所以實際上不是真的渲染整個DOM樹。這個Virtual DOM是一個純粹的JS數(shù)據(jù)結(jié)構(gòu),所以性能會比原生DOM快很多。

Virtual DOM 本質(zhì)上就是在 JS 和 DOM 之間做了一個緩存。可以類比 CPU 和硬盤,既然硬盤這么慢,我們就在它們之間加個緩存:既然 DOM 這么慢,我們就在它們 JS 和 DOM 之間加個緩存。CPU(JS)只操作內(nèi)存(Virtual DOM),最后的時候再把變更寫入硬盤(DOM)

Virtual DOM 算法:

  • 觸發(fā)相應(yīng)組件render方法

  • 重新構(gòu)建新的虛擬DOM樹

  • 將當(dāng)前新的虛擬DOM樹和上一次的舊樹進(jìn)行對比

  • 得到DOM結(jié)構(gòu)的區(qū)別,計算出最小變化集,進(jìn)行實際的瀏覽器DOM更新(批量更新)。

Data Flow

數(shù)據(jù)如何存放,如何更改數(shù)據(jù),如何通知數(shù)據(jù)更改等等,單向響應(yīng)的數(shù)據(jù)流,從而減少了重復(fù)代碼,這也是它為什么比傳統(tǒng)數(shù)據(jù)綁定更簡單。

接受輸入數(shù)據(jù)(通過 this.props ),組件還可以保持內(nèi)部狀態(tài)數(shù)據(jù)(通過 this.state )當(dāng)一個組件的狀態(tài)數(shù)據(jù)的變化。

React 組件之間交流的方式

組件免不了要與用戶互動,React 的一大創(chuàng)新,就是將組件看成是一個狀態(tài)機(jī),一開始有一個初始狀態(tài),然后用戶互動,導(dǎo)致狀態(tài)變化,從而觸發(fā)重新渲染 UI。

  • 【父組件】向【子組件】傳值;
    父組件的數(shù)據(jù)可以通過設(shè)置子組件的props傳遞數(shù)據(jù)給子組件
  • 【子組件】向【父組件】傳值;
    可以在父組件中傳一個callback(回調(diào)函數(shù))給子組件,子組件內(nèi)調(diào)用這個callback即可改變父組件的數(shù)據(jù)
  • 兄弟組件之間傳值
    沒有任何嵌套關(guān)系的組件之間傳值(PS:比如:兄弟組件之間傳值)
    當(dāng)兩個組件不是父子關(guān)系,但有相同的父組件時,將這兩個組件稱為兄弟組件。兄弟組件不能直接相互傳送數(shù)據(jù),此時可以將數(shù)據(jù)掛載在父組件中,由兩個組件共享:如果組件需要數(shù)據(jù)渲染,則由父組件通過props傳遞給該組件;如果組件需要改變數(shù)據(jù),則父組件傳遞一個改變數(shù)據(jù)的回調(diào)函數(shù)給該組件,并在對應(yīng)事件中調(diào)用。

寫了個簡單的例子如下:

//孫子,將下拉選項的值傳給爺爺
var Grandson = React.createClass({
    render: function(){
        return (
            <div>性別:
                <select onChange={this.props.handleSelect}>
                    <option value="男">男</option>
                    <option value="女">女</option>
                </select>
            </div>
        )
    }
});
//子,將用戶輸入的姓名傳給爹  
//對于孫子的處理函數(shù),父只需用props傳下去即可
var Child = React.createClass({
    render: function(){
        return (
            <div>
                姓名:<input onChange={this.props.handleVal}/>
                <Grandson handleSelect={this.props.handleSelect}/>
            </div>
        )
    }
});
//父組件,準(zhǔn)備了兩個state,username和sex用來接收子孫傳過來的值,對應(yīng)兩個函數(shù)handleVal和handleSelect
var Parent = React.createClass({
    getInitialState: function(){
        return {
            username: '',
            sex: ''
        }
    },
    handleVal: function(event){
        this.setState({username: event.target.value});
    },
    handleSelect: function(event) {
        this.setState({sex: event.target.value});
    },
    render: function(){
        return (
            <div>
                <div>用戶姓名:{this.state.username}</div>
                <div>用戶性別:{this.state.sex}</div>
                <Child handleVal={this.handleVal.bind(this)} handleSelect={this.handleSelect.bind(this)}/>
            </div>
        )
    }
});
React.render(
  <Parent />,
  document.getElementById('test')
);
  • Props 傳遞數(shù)據(jù)

如果組件嵌套層次太深,那么從外到內(nèi)組件的交流成本就變得很高,通過 props 傳遞值的優(yōu)勢就不那么明顯了。所以個人建議盡可能的減少組件的層次,就像寫 HTML 一樣,簡單清晰的結(jié)構(gòu)更惹人愛)

  • 難的地方
    在創(chuàng)建應(yīng)用的每一個 state之前要做的:
  • 確定每一個需要這個 state 來渲染的組件。
  • 找到一個公共所有者組件(一個在層級上高于所有其他需要這個 state 的組件的組件)
  • 這個公共所有者組件或另一個層級更高的組件應(yīng)該擁有這個 state。
  • 如果你沒有找到可以擁有這個 state 的組件,創(chuàng)建一個僅用來保存狀態(tài)的組件并把它加入比這個公共所有者組件層級更高的地方
  • 當(dāng)應(yīng)用足夠復(fù)雜時才能體會到它的好處,雖然在一般應(yīng)用場景下你可能不會意識到它的存在

兄弟組件的溝通的解決方案就是找到兩個組件共同的父組件,一層一層的調(diào)用上一層的回調(diào),再一層一層地傳遞props。如果組件樹嵌套太深,就會出現(xiàn)如下慘不忍睹的組件親戚調(diào)用圖

  • 全局事件
    Context 使用上下文可以讓子組件直接訪問祖先的數(shù)據(jù)或函數(shù),無需從祖先組件一層層地傳遞數(shù)據(jù)到子組件中

第三方的類庫

React讓我們有很大的自由度去挑選第三方的類庫,比如

Redux

Redux是一種組織代碼的推薦思想,就像 “引導(dǎo)數(shù)據(jù)流流向的導(dǎo)流管”,解決上面那幅關(guān)系復(fù)雜的親戚圖問題

  • 整個應(yīng)用只有唯一一個可信數(shù)據(jù)源,也就是只有一個Store,如 Redux 限定一個應(yīng)用中只能有單一的 store,這樣的限定能夠讓應(yīng)用中數(shù)據(jù)結(jié)果集中化,提高可控性

  • Action、Reducer、及 Store

  • State 只能通過觸發(fā)Action 來更改

  • State 的更改必須寫成純函數(shù),也就是每次更改總是返回一個新的State,在*Redux 里這種函數(shù)稱為Reducer

Redux 對于組件間的解耦提供了很大的便利,如果你在考慮該不該使用 Redux 的時候,社區(qū)里有一句話說,“當(dāng)你不知道該不該使用 Redux 的時候,那就是不需要的”

Redux 用起來一時爽,重構(gòu)或者將項目留給后人的時候,就是個大坑,Redux 中的 dispatch 和 subscribe 方法遍布代碼的每一個角落。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,643評論 2 380

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