React筆記

關于JSX

考慮這樣一段代碼:
const element = <h1>Hello, world!</h1>;
這段代碼既不是字符串也不是HTML,它是JSX,是javascript的拓展。在React里用來描述UI,因為JSX是用來生產React內的“元素”的。以后會介紹它是如何被渲染成DOM的。

我們可以在JSX里嵌入任何的javascript代碼,例如下面這個例子就混合了javascript代碼:
function formatName(user) {    return user.firstName + ' ' + user.lastName; } const user = {    firstName: 'Harper',    lastName: 'Perez' }; const element = ( <h1> Hello, {formatName(user)}! </h1>);  ReactDOM.render(    element,    document.getElementById('root')  );
需要指出的是,雖然JSX是javascript的拓展,但是實際到最后,JSX還是要被編譯成純粹的javascript對象,所以在if語句、for循環語句里面都可以用,而且還可以把它當成函數參數或返回值。例如:
function getGreeting(user) { if (user) { return <h1>Hello, {formatName(user)}!</h1>; } return <h1>Hello, Stranger.</h1>; }
鑒于JSX相比HTML更接近javascript,所以它里面元素的屬性采用駝峰命名法,例如class要寫成className,tabindex要寫成tabIndex。

JSX自帶過濾以避免XSS攻擊,所以不必擔心注入的問題。

元素渲染

元素是React應用里最小的構造塊,一個元素所描述的內容決定了你在屏幕上看到的東西。比如這個:
const element = <h1>Hello, world</h1>;
另外,不同于瀏覽器DOM元素,React中的元素都是純粹的對象,創建起來代價很低,而且React會負責更新瀏覽器DOM以便于和React元素相匹配。

React中的元素是不可變的,一旦創建了一個元素,就無法改變它的屬性或者是后代元素,這有點類似電影中的一幀,僅是某個時間點的快照。就目前的知識來說,如果想要更新UI,唯一的方法就是再創建一個新的元素,比如我想制作一個顯示時間的程序,要做到內容每秒刷新一次,那么以目前的知識來說,只能這么寫代碼:
function tick() { const element = ( <div> <h1>Hello, world!</h1> <h2>It is {new Date().toLocaleTimeString()}.</h2> </div> ); ReactDOM.render( element, document.getElementById('root') ); } setInterval(tick, 1000);
值得注意的是,雖然每秒鐘我們創建一個新的元素,但React會十分智能的分辨其中的不同,每次僅僅會改變不同的部分,以上面的代碼為例,會發現每次只改變了dom中的文字。

變化圖
變化圖

在React程序中大部分代碼都是一次性的,不牽扯到動態刷新的問題,不過在后面會介紹如何利用狀態組件解決這個問題。

組件

組件給了你將UI切割的能力,以便更好地復用UI代碼。從概念上看,組件和javascript中的函數很像,可以接受任意的輸入然后返回一個最終繪制在屏幕上的React元素。

創建組件的最簡單方式就是寫一個javascript函數:
function Welcome(props) { return <h1>Hello, {props.name}</h1>; }

之前的React元素都采用的是原生的dom標簽,比如div,其實React提供了一種機制來滿足我們采用自定義標簽,例如:
function Welcome(props) { return <h1>Hello, {props.name}</h1>; } const element = <Welcome name="Sara" />; ReactDOM.render( element, document.getElementById('root') );
解釋React對一下上面這段代碼干了什么:

  1. <Welcome name="Sara" />元素調用ReactDOM.render()方法。
  2. React以{name: 'Sara'}為props調用Welcome組件。
  3. Welcome組件返回了一個<h1>Hello, Sara</h1>元素。
  4. React高效的更新DOM。

需要注意的是組件均是以大寫字母開頭,比如<div/>就是一個dom標簽,而<Welcome/>就是一個組件,而且需要在作用于中有一個對應的Welcome函數。

需要指出的是,組件可以嵌套,例如:
function Welcome(props) { return <h1>Hello, {props.name}</h1>; } function App() { return ( <div> <Welcome name="Sara" /> <Welcome name="Cahal" /> <Welcome name="Edite" /> </div> ); } ReactDOM.render( <App />, document.getElementById('root') );

注意:組件返回的必須是一個元素,例如上面的代碼中,雖然希望的是三個Welcome元素,但是必須用一個div包裹起來,不能直接返回三個元素。

鑒于組件的可嵌套能力, React推薦將一個大組件分割成小組件,例如下面這個組件:
function Comment(props) { return ( <div className="Comment"> <div className="UserInfo"> <img className="Avatar" src={props.author.avatarUrl} alt={props.author.name} /> <div className="UserInfo-name"> {props.author.name} </div> </div> <div className="Comment-text"> {props.text} </div> <div className="Comment-date"> {formatDate(props.date)} </div> </div> ); }
現在演示一下如何拆分這個組件,首先,可以提取出Avatar
function Avatar(props) { return ( <img className="Avatar" src={props.user.avatarUrl} alt={props.user.name} /> ); }
然后提取Comment:
function Comment(props) { return ( <div className="Comment"> <UserInfo user={props.author} /> <div className="Comment-text"> {props.text} </div> <div className="Comment-date"> {formatDate(props.date)} </div> </div> ); }
提取工作一開始看起來可能是乏味的,但是在大型應用中,這可以極大的提高代碼復用率。一個指導原則是,如果一個UI要素多次出現或者太大,都要做拆分以便復用。

有一點需要說明的是,組件的props都是只讀的,不要想在組件中修改props,那樣不會起任何作用。

生命周期

思考一下上面演示的時鐘程序,到目前為止只介紹了一種更新UI的方法,也就是調用ReactDOM.render()去重新渲染,如下所示:
function tick() { const element = ( <div> <h1>Hello, world!</h1> <h2>It is {new Date().toLocaleTimeString()}.</h2> </div> ); ReactDOM.render( element, document.getElementById('root') ); } setInterval(tick, 1000);
再介紹新方法前,我們先將這段代碼利用React的組件化能力進行封裝:
function Clock(props) { return ( <div> <h1>Hello, world!</h1> <h2>It is {props.date.toLocaleTimeString()}.</h2> </div> ); } function tick() { ReactDOM.render( <Clock date={new Date()} />, document.getElementById('root') ); } setInterval(tick, 1000);
僅僅做到這樣是不夠的,我們希望將更改時間的邏輯和元素綁定到一起,去除掉setInterval,最終的理想效果應該這樣的:
ReactDOM.render( <Clock />, document.getElementById('root') );
為了到達理想的效果,我們需要為組件添加state。要想使用state就需要講組件用class的寫法,因為class寫法在定義class時可以提供一些額外的特性,state就是其中之一。
將函數化的組件轉化成class的寫法,可以按照下面的步驟進行:

  1. 創建一個同名的ES6 class,該class繼承自React.Component
  2. 添加一個空的render方法。
  3. 將函數的主體部分挪到render方法內。
  4. 用this.props替換props。
  5. 將原來的函數形組件刪掉。

上述操作完成后函數就轉換為了class:
class Clock extends React.Component { render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.props.date.toLocaleTimeString()}.</h2> </div> ); } }
接下來就能用statelifecycle hooks這兩項技術,首先用state代替props
class Clock extends React.Component { render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } }
然后添加一個構造函數去初始化this.state:
class Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } }
需要注意的是,構造函數的參數是props并且將這個參數傳給了基類。
接下來可以將date(prop)從Clock元素上移除了。
ReactDOM.render( <Clock />, document.getElementById('root') );
此時我們的組件是這樣的:
class Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } } ReactDOM.render( <Clock />, document.getElementById('root') );
接下來我們將為Clock設置一個定時器,這需要我們了解React組件生命周期的知識,React中的組件會經歷從創建到移除的一個周期,暴露給我們的是兩個鉤子函數:componentDidMount和componentWillUnmount,分別代表創建和移除,在這兩個時間點上我們可以做一些工作。例如定義計時器:
class Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } componentDidMount() { this.timerID = setInterval( () => this.tick(), 1000 ); } componentWillUnmount() { clearInterval(this.timerID); } tick() { this.setState({ date: new Date() }); } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } } ReactDOM.render( <Clock />, document.getElementById('root') );

需要注意的是,在更新date數值的時候,不能直接賦值,而是采用this.setState方法進行賦值。不過在構造函數里給date初始化的時候可以直接賦值。

事件處理

React里的事件名稱采用的是駝峰拼寫法,比如HTML中是onclick,在React里就是onClick。另外不能通過返回false來阻止默認事件,而應該用preventDefault方法。

感覺不管是React還是Angular,在工具性上都比較差,可能需要其它附件做為拓展吧,總之工具性的都比較差,常常有不夠用的感覺。

有條件的渲染

在React里面,可以根據實際情況有選擇性的渲染,例如考慮下面兩個組件:
function UserGreeting(props) { return <h1>Welcome back!</h1>; } function GuestGreeting(props) { return <h1>Please sign up.</h1>; }
然后創建一個新的組件,來有對上面兩個組件做選擇:
function Greeting(props) { const isLoggedIn = props.isLoggedIn; if (isLoggedIn) { return <UserGreeting />; } return <GuestGreeting />; } ReactDOM.render( // Try changing to isLoggedIn={true}: <Greeting isLoggedIn={false} />, document.getElementById('root') );
React支持將元素變量化以便根據不同的情況渲染不同的元素,如下:
function LoginButton(props) { return ( <button onClick={props.onClick}> Login </button> ); } function LogoutButton(props) { return ( <button onClick={props.onClick}> Logout </button> ); } class LoginControl extends React.Component { constructor(props) { super(props); this.handleLoginClick = this.handleLoginClick.bind(this); this.handleLogoutClick = this.handleLogoutClick.bind(this); this.state = {isLoggedIn: false}; } handleLoginClick() { this.setState({isLoggedIn: true}); } handleLogoutClick() { this.setState({isLoggedIn: false}); } render() { const isLoggedIn = this.state.isLoggedIn; let button = null; if (isLoggedIn) { button = <LogoutButton onClick={this.handleLogoutClick} />; } else { button = <LoginButton onClick={this.handleLoginClick} />; } return ( <div> <Greeting isLoggedIn={isLoggedIn} /> {button} </div> ); } } ReactDOM.render( <LoginControl />, document.getElementById('root') );
注意其中button變量的用法。
同時,在組件中還可以用javascript邏輯控制語句,如:
function Mailbox(props) { const unreadMessages = props.unreadMessages; return ( <div> <h1>Hello!</h1> {unreadMessages.length > 0 && <h2> You have {unreadMessages.length} unread messages. </h2> } </div> ); } const messages = ['React', 'Re: React', 'Re:Re: React']; ReactDOM.render( <Mailbox unreadMessages={messages} />, document.getElementById('root') );
React的組件也可以用condition ? true : false語句,如:
render() { const isLoggedIn = this.state.isLoggedIn; return ( <div> The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in. </div> ); }
或者更復雜一些的:
render() { const isLoggedIn = this.state.isLoggedIn; return ( <div> {isLoggedIn ? ( <LogoutButton onClick={this.handleLogoutClick} /> ) : ( <LoginButton onClick={this.handleLoginClick} /> )} </div> ); }
如果不希望組件渲染出內容,可以返回null;

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

推薦閱讀更多精彩內容

  • react 基本概念解析 react 的組件聲明周期 react 高階組件,context, redux 等高級...
    南航閱讀 1,080評論 0 1
  • 最近看了一本關于學習方法論的書,強調了記筆記和堅持的重要性。這幾天也剛好在學習React,所以我打算每天堅持一篇R...
    gaoer1938閱讀 1,702評論 0 5
  • 本筆記基于React官方文檔,當前React版本號為15.4.0。 1. 安裝 1.1 嘗試 開始之前可以先去co...
    Awey閱讀 7,771評論 14 128
  • 深入JSX date:20170412筆記原文其實JSX是React.createElement(componen...
    gaoer1938閱讀 8,091評論 2 35
  • 運營自媒體已有一些時日,從剛剛注冊頭條號開始,到自媒體平臺都有自己的文章。一路走來有喜有憂,未來不知道自媒體這條路...
    風華真年少閱讀 162評論 0 0