1 什么是component
設計接口的時候,把通用的設計元素(按鈕,表單框,布局組件等)拆成接口良好定義的可復用的組件。 這樣,下次開發相同界面程序時就可以寫更少的代碼,也意義着更高的開發效率,更少的 Bug 和更少的程序體積。 Thinking in react 里面舉了一個React 構建可搜索的商品數據的例子。
接下來我們將創建一個好玩的component來全面了解它的組成和運行機制, 回顧一下我們在 001 中的 public/index.html 它是直接引用了webpack生成的client.min.js(console里面打印文字)。 這樣一點都不好玩,接來下我們從頭開始構造一個完成的index.hmtl頁面.

2. 定義html和引用的js入口
<html> <head> <meta charset="utf-8"> <title>React's Component Example</title> </head> <body> <div id="app"></div> <script src="client.min.js" type="text/javascript"></script> </body> </html>
在html里面定義一個id= "app" 的 div標簽並引用client.min.js,這個html就是我們的入口。 時刻記得client.min.js是來自於webpack的entry: src/client.js 里面生成的。 所以我們就通過client.js來操作html中的div構建頁面。
import React from 'react'; import ReactDom from "react-dom"; import Layout from "./component/Layout"; const app = document.getElementById("app"); ReactDom.render(<Layout />, app);
1. 為什么要使用JSX語法? 2. JSX和HTML的差異是什么?
這里你只需要知道 <Layout /> 是引用Layout Component。
頁面的構成應該是(頁面就是一個Tree,不要什么都堆在一起)
–Layout -—Header –title -—Body -—Footer
2.1. 定義最上層的Layout
import React from 'react'; import Footer from "./Footer"; import Header from "./Header"; export default class Layout extends React.Component { render() { return (<div> <Header /> <Footer /> </div>); }; }
component 必須要定義的render函數,render是核心, 它組裝生成這個組件的HTML結構(使用原生HTML標簽或者子組件)。
當調用的時候,會檢測 this.props 和 this.state,返回一個單子級組件(注意只能是單個,所以我們用div把header和footer包了一層)。
該子級組件可以是虛擬的本地 DOM 組件(比如 <div /> 或者 React.DOM.div()),也可以是自定義的復合組件。
你也可以返回 null 或者 false 來表明不需要渲染任何東西。 實際上,react 渲染一個 <noscript> 標簽來處理當前的差異檢查邏輯。當返回 null 或者 false 的時候,this.getDOMNode() 將返回 null。
render() 函數應該是純粹的,也就是說該函數不修改組件state, 每次調用都返回相同的結果,不讀寫 DOM 信息,也不和瀏覽器交互(例如通過使用setTimeout)。 如果需要和瀏覽器交互,在 componentDidMount() 中或者其它生命周期方法中做這件事。保持 render() 純粹,可以使服務器端渲染更加切實可行,也使組件更容易被理解。
2.2. 定義Footer
import React from "react"; export default class Footer extends React.Component { render() { return(<div> <h3> Footer: Building UI is a funny thing </h3> </div>);} }
只是簡單的把自己用h1顯示出來
2.3. 定義Header
import React from "react"; import Title from "./Title"; export default class Header extends React.Component { render() { return(<div> <Title /> <h3> Header: Component create a new world </h3> </div> ); } }
我們就可以看到它引用了Title
2.4. 定義Title
import React from "react"; export default class Title extends React.Component { render() { return(<div> <h2> Title: Everything is Component </h2> </div> ); } }
這上面都是靜態的東西,根本就看不出component有什么值得炫酷的地方,只是顯示了 title header footer。 離標題Title隨着我們給定的輸入框的文字變化還差得遠 不過在開始之前, 應該先預習一下component api弄明白它的state和prop。
1. props就是component的屬性,由外部通過JSX屬性傳入設置,一旦初始設置完成,就可以認為this.props是不可更改的,
所以不要輕易更改設置this.props里面的值(雖然對於一個JS對象你可以做任何事)。
2. state是component的當前狀態,可以把component簡單看成一個“狀態機”,根據狀態state 呈現不同的UI展示。
一旦狀態(數據)更改,component就會自動調用render重新渲染UI,這個更改的動作會通過 this.setState方法來觸發。
3. 一條原則:讓component盡可能地少狀態。這樣組件邏輯就越容易維護。當更改這個狀態(數據)需要更新組件UI的就可以認為是state
4. 無狀態compoent, 我們上面的4個就是這種無狀態的。
你也可以用純粹的函數來定義無狀態的組件(stateless function), 這種組件沒有狀態,沒有生命周期,只是簡單的接受props 渲染生成DOM 結構。無狀態組件非常簡單,開銷很低,如果可能的話盡量使用無狀態組件。
3. 有狀態的component
3.1 父子間通信就是通過props屬性來傳遞, 在父component中給子component設置props,然后子component就可以訪問到父component的數據和方法.
3.2 非父子間通信使用全局事件Pub/Sub模式,在componentDidMount里面訂閱事件,在 componentWillUnmount里面取消訂閱,當收到事件觸發的時候調用setState更新UI。
在Layout中把title和changetitle的函數都傳給header component, 注意傳給下一級方法時一定要顯示的表明這個方法來自己於哪里 — bind(this)操作。
import React from 'react'; import Footer from "./Footer"; import Header from "./Header"; export default class Layout extends React.Component { constructor(){ super(); this.state = {title: "welcome"}; } changeTitle(title){ this.setState({title: title}); } render() { return (<div> <Header changeTitle={this.changeTitle.bina(this)} title={this.state.title}/> <Footer /> </div>); }; }
Header接收到這個Title后如何處理
import React from "react"; import Title from "./Title"; export default class Header extends React.Component { handleChange(e) { const title = e.target.value; this.props.changeTitle(title); } render() { return(<div> <Title title={this.props.title}/> <input value={this.props.title} onChange= {this.handleChange.bind(this)}/> <input value={this.props.title} onChange= {this.handleChange.bind(this)}/> </div> ); } }
Header把title又往下傳遞給應該處理的Title Component, 自己又建了兩個輸入框,初始值為title,然后再定義一個onChange事件
Title接到title后只需要把它顯示成title就行了,這就是它要做的事!
import React from "react"; export default class Title extends React.Component { render() { return(<div> <h2> {this.props.title} </h2> </div> ); } }
總結上面的流程就是Layout把自己changeTitle的方法和title的屬性先傳給Header,Header再把他們傳給Title。
這里發生了一件非常奇妙的事,我們在Layout Header Title 里都有state, 我們通過input輸入框輸入文字觸發Layout的SetState方法, 結果所有的State里面的title都跟着變化啦,這就是virtual DOM的好處,react把操作真實DOM的操作又封裝了一層,讓我們不用操心哪一個DOM應該更新這種事。 JS很快,慢的只是刷新DOM里面的tree, 而且繁鎖, 但是react把刷新DOM的操作透明了,簡直太貼心啦。
我們打開chrome developer tool 里面timeline的 Rendering下的Enable paint flashing,看看我們在輸入的時候是哪一些DOM刷新啦。

可以看出它只是刷新應該刷新的Title Header和input
通過這一章節我們掌握到了什么是component, component的state props及他們之前的區別,父子component之間如何通信。
4 擴展閱讀
