Babel 轉譯器 這是react自帶的一個編譯器javascript
props和states一個是組件外部的,一個是組件內部的css
組件能夠將UI切分紅一些的獨立的、可複用的部件,這樣你就只需專一於構建每個單獨的部件。html
組件從概念上看就像是函數,它能夠接收任意的輸入值(稱之爲「props」),並返回一個須要在頁面上展現的React元素。java
定義一個組件最簡單的方式是使用JavaScript函數:react
function Welcome(props) { return <h1>Hello, {props.name}</h1>; }
該函數是一個有效的React組件,它接收一個單一的「props」對象並返回了一個React元素。咱們之因此稱這種類型的組件爲函數定義組件,是由於從字面上來看,它就是一個JavaScript函數。git
你也可使用 ES6 class 來定義一個組件:github
class Welcome extends React.Component { render() { return <h1>Hello, {this.props.name}</h1>; } }
上面兩個組件在React中是相同的。api
咱們將在下一節討論類的一些額外特性。在那以前,咱們都將使用較爲簡潔的函數定義組件。網絡
在前面,咱們遇到的React元素都只是DOM標籤:app
const element = <div />;
然而,React元素也能夠是用戶自定義的組件:
const element = <Welcome name="Sara" />;
當React遇到的元素是用戶自定義的組件,它會將JSX屬性做爲單個對象傳遞給該組件,這個對象稱之爲「props」。
例如,這段代碼會在頁面上渲染出」Hello,Sara」:
function Welcome(props) { return <h1>Hello, {props.name}</h1>; } const element = <Welcome name="Sara" />; ReactDOM.render( element, document.getElementById('root') );
咱們來回顧一下在這個例子中發生了什麼:
<Welcome name="Sara" />
元素調用了ReactDOM.render()
方法。{name: 'Sara'}
做爲props傳入並調用Welcome
組件。Welcome
組件將<h1>Hello, Sara</h1>
元素做爲結果返回。<h1>Hello, Sara</h1>
。警告:
組件名稱必須以大寫字母開頭。
例如,
<div />
表示一個DOM標籤,但<Welcome />
表示一個組件,而且在使用該組件時你必須定義或引入它。
組件能夠在它的輸出中引用其它組件,這就可讓咱們用同一組件來抽象出任意層次的細節。在React應用中,按鈕、表單、對話框、整個屏幕的內容等,這些一般都被表示爲組件。
例如,咱們能夠建立一個App
組件,用來屢次渲染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') );
一般,一個新的React應用程序的頂部是一個App
組件。可是,若是要將React集成到現有應用程序中,則能夠從下而上使用像Button
這樣的小組件做爲開始,並逐漸運用到視圖層的頂部。
警告:
組件的返回值只能有一個根元素。這也是咱們要用一個
<div>
來包裹全部<Welcome />
元素的緣由。
你能夠將組件切分爲更小的組件,這沒什麼好擔憂的。
例如,來看看這個Comment
組件:
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> ); }
這個組件接收author
(對象)、text
(字符串)、以及date
(Date對象)做爲props,用來描述一個社交媒體網站上的評論。
這個組件因爲嵌套,變得難以被修改,可複用的部分也難以被複用。因此讓咱們從這個組件中提取出一些小組件。
首先,咱們來提取Avatar
組件:
function Avatar(props) { return ( <img className="Avatar" src={props.user.avatarUrl} alt={props.user.name} /> ); }
Avatar
做爲Comment
的內部組件,不須要知道是否被渲染。所以咱們將author
改成一個更通用的名字user
。
咱們建議從組件自身的角度來命名props,而不是根據使用組件的上下文命名。
如今咱們能夠對Comment
組件作一些小小的調整:
function Comment(props) { return ( <div className="Comment"> <div className="UserInfo"> <Avatar user={props.author} /> <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> ); }
接下里,咱們要提取一個UserInfo
組件,用來渲染Avatar
旁邊的用戶名:
function UserInfo(props) { return ( <div className="UserInfo"> <Avatar user={props.user} /> <div className="UserInfo-name"> {props.user.name} </div> </div> ); }
這可讓咱們進一步簡化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中有一部分重複使用了好幾回(好比,Button
、Panel
、Avatar
),或者其自身就足夠複雜(好比,App
、FeedStory
、Comment
),相似這些都是抽象成一個可複用組件的絕佳選擇,這也是一個比較好的作法。
不管是使用函數或是類來聲明一個組件,它決不能修改它本身的props。來看這個sum
函數:
function sum(a, b) { return a + b; }
相似於上面的這種函數稱爲「純函數」,它沒有改變它本身的輸入值,當傳入的值相同時,老是會返回相同的結果。
與之相對的是非純函數,它會改變它自身的輸入值:
function withdraw(account, amount) { account.total -= amount; }
React是很是靈活的,但它也有一個嚴格的規則:
全部的React組件必須像純函數那樣使用它們的props。
固然,應用的界面是隨時間動態變化的,咱們將在下一節介紹一種稱爲「state」的新概念,State能夠在不違反上述規則的狀況下,根據用戶操做、網絡響應、或者其餘狀態變化,使組件動態的響應並改變組件的輸出。
this.props
對象的屬性與組件的屬性一一對應,可是有一個例外,就是 this.props.children
屬性。它表示組件的全部子節點(查看 demo05
)。
var NotesList = React.createClass({ render: function() { return ( <ol> { React.Children.map(this.props.children, function (child) { return <li>{child}</li>; }) } </ol> ); } }); ReactDOM.render( <NotesList> <span>hello</span> <span>world</span> </NotesList>, document.body );
上面代碼的 NoteList
組件有兩個 span
子節點,它們均可以經過 this.props.children
讀取,運行結果以下。
這裏須要注意, this.props.children
的值有三種可能:若是當前組件沒有子節點,它就是 undefined
;若是有一個子節點,數據類型是 object
;若是有多個子節點,數據類型就是 array
。因此,處理 this.props.children
的時候要當心。
React 提供一個工具方法 React.Children
來處理 this.props.children
。咱們能夠用 React.Children.map
來遍歷子節點,而不用擔憂 this.props.children
的數據類型是 undefined
仍是 object
。更多的 React.Children
的方法,請參考官方文檔。
Hello.js
import React from 'react'; export default ({ name }) => <h1>Hello {name}!</h1>;
<div id="root"></div>
import React, { Component } from 'react'; import { render } from 'react-dom'; import Hello from './Hello'; import './style.css'; import Name from './Name'; class App extends Component { constructor() { super(); this.state = { name: 'React1', insertName: 'xss', hasNameFlag: true }; console.log("indexconstructor"); } changeName = () => { this.setState({ insertName: 'yys', }) } deleteName = () => { this.setState({ hasNameFlag: false }) } compomentwillMount() { console.log("indexcompomentwillMount");//dom放進去以前,只執行一次 } componentDidMount() { console.log("indexcomponentDidMount");//dom放進去以後,只執行一次 } componentWillReceiveProps(nextProps) {//有改變就執行,修改states的時候用到 console.log("indexcomponentWillReceiveProps"); } componentwillupDate() {//dom更新以前 console.log("indexcomponentwillupDate"); } componentDidUpdate() { console.log("indexcomponentDidUpdate");//dom更新以後 } componentWillUnmount() { console.log("indexcomponentWillUnmount");// dom組件被移除以前被調用,能夠用於作一些清理工做 } render() { console.log("indexrender"); const { insertName, hasNameFlag } = this.state; const a = ( <h1 className={"greeting"} title="yys"> Hello, world! </h1> ); let name = null; if (hasNameFlag) { name = <Name name={insertName} > <span>world</span></Name>; } return ( <div> <Hello name={this.state.name} /> {name} <button onClick={this.changeName}>更名</button> <button onClick={this.deleteName}>刪除</button> <p> Start editing to see some magic happen :) </p> </div> ); } } render(<App />, document.getElementById('root'));
Name.js
import React, { Component } from 'react'; class Name extends Component { constructor(props) { super(props); this.state = { name: props.name }; // console.log(this) console.log("constructor"); } compomentwillMount() { console.log("compomentwillMount"); } componentDidMount() { console.log("componentDidMount"); } componentWillReceiveProps(nextProps) { if (nextProps.name === this.props.name) return; this.setState({ name: nextProps.name }) console.log("componentWillReceiveProps"); } componentwillupDate() { console.log("componentwillupDate"); } componentDidUpdate() { console.log("componentDidUpdate"); } componentWillUnmount() { console.log("componentWillUnmount"); } render() { console.log("render"); const { name } = this.state; return <h2>{name}</h2>; } } export default Name;
第一次刷新進入時:
點擊修改時
點擊刪除時:
constructor 1 只執行一次
compomentwillMount 1 dom放進去以前,也是隻執行一次
componentDidMount 1 dom放進去以後,也是隻執行一次
componentwillReceIveProps 有改變就執行,修改states的時候用到
componentwillupDate dom更新以前
componentDidupDate dom更新以後
OK. so here goes. If you have a simple structure with a parent and 2 children like this:
<Parent> <Child/> <Child/> </Parent>
Then the sequence of events firing will be:
<Parent> componentWillMount()
<Parent> render()
, which starts to render children<Child> componentWillMount()
of the first child<Child> render()
of the first child<Child> componentWillMount()
of the second child<Child> render()
of the second child<Child> componentDidMount()
of the first child (these will start only after the last render in the tree has run)<Child> componentDidMount()
of the second child<Parent> componentDidMount()
(this one will run only after its last child has run componentDidMount
)