小白一枚,覺得阮一峰老師寫的react入門非常簡單易懂,所以就轉載過來了。
等到我自己后面能力變強之后,我就自己寫一篇給新手看的react教程!
一,HTML模板
<!DOCTYPE html>
<html>
<head>
<script src="../build/react.js"></script>
<script src="../build/react-dom.js"></script>
<script src="../build/browser.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/babel">
// ** Our code goes here! **
</script>
</body>
</html>
上面代碼有兩個地方需要注意。首先,最后一個 <script> 標簽的 type 屬性為 text/babel 。這是因為 React 獨有的 JSX 語法,跟 JavaScript 不兼容。凡是使用 JSX 的地方,都要加上 type="text/babel" 。
其次,上面代碼一共用了三個庫: react.js 、react-dom.js 和 Browser.js ,它們必須首先加載。其中,react.js 是 React 的核心庫,react-dom.js 是提供與 DOM 相關的功能,Browser.js 的作用是將 JSX 語法轉為 JavaScript 語法,這一步很消耗時間,實際上線的時候,應該將它放到服務器完成。
$ babel src --out-dir build
上面命令可以將 src 子目錄的 js 文件進行語法轉換,轉碼后的文件全部放在 build 子目錄。
二,ReactDOM.render()
ReactDOM.render 是 React 的最基本方法,用于將模板轉為 HTML 語言,并插入指定的 DOM 節點。
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('example')
);
上面代碼將一個 h1 標題,插入 example 節點。
三、JSX 語法
上一節的代碼, HTML 語言直接寫在 JavaScript 語言之中,不加任何引號,這就是 [JSX 的語法],它允許 HTML 與 JavaScript 的混寫。
var names = ['Alice', 'Emily', 'Kate'];
ReactDOM.render(
<div>
{
names.map(function (name) {
return <div>Hello, {name}!</div>
})
}
</div>,
document.getElementById('example')
);
上面代碼體現了 JSX 的基本語法規則:遇到 HTML 標簽(以 < 開頭),就用 HTML 規則解析;遇到代碼塊(以 { 開頭),就用 JavaScript 規則解析。上面代碼的運行結果如下。
JSX 允許直接在模板插入 JavaScript 變量。如果這個變量是一個數組,則會展開這個數組的所有成員。
var arr = [
<h1>Hello world!</h1>,
<h2>React is awesome</h2>,
];
ReactDOM.render(
<div>{arr}</div>,
document.getElementById('example')
);
上面代碼的arr變量是一個數組,結果 JSX 會把它的所有成員,添加到模板,運行結果如下。
四、組件
React 允許將代碼封裝成組件(component),然后像插入普通 HTML 標簽一樣,在網頁中插入這個組件。React.createClass 方法就用于生成一個組件類。
var HelloMessage = React.createClass({
render: function() {
return <h1>Hello {this.props.name}</h1>;
}
});
ReactDOM.render(
<HelloMessage name="John" />,
document.getElementById('example')
);
上面代碼中,變量 HelloMessage 就是一個組件類。模板插入 <HelloMessage /> 時,會自動生成 HelloMessage 的一個實例(下文的"組件"都指組件類的實例)。所有組件類都必須有自己的 render 方法,用于輸出組件。
注意,組件類的第一個字母必須大寫,否則會報錯,比如HelloMessage不能寫成helloMessage。另外,組件類只能包含一個頂層標簽,否則也會報錯。
var HelloMessage = React.createClass({
render: function() {
return <h1>
Hello {this.props.name}
</h1><p>
some text
</p>;
}
});
上面代碼會報錯,因為HelloMessage組件包含了兩個頂層標簽:h1和p。
組件的用法與原生的 HTML 標簽完全一致,可以任意加入屬性,比如 <HelloMessage name="John"> ,就是 HelloMessage 組件加入一個 name 屬性,值為 John。組件的屬性可以在組件類的 this.props 對象上獲取,比如 name 屬性就可以通過 this.props.name 讀取。上面代碼的運行結果如下。
添加組件屬性,有一個地方需要注意,就是 class 屬性需要寫成 className ,for 屬性需要寫成 htmlFor ,這是因為 class 和 for 是 JavaScript 的保留字。
五、this.props.children
this.props 對象的屬性與組件的屬性一一對應,但是有一個例外,就是 this.props.children 屬性。它表示組件的所有子節點。
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 提供一個工具方法來處理this.props.children。我們可以用React.Children.map來遍歷子節點,而不用擔心this.props.children的數據類型是undefined還是object。
六、PropTypes
組件的屬性可以接受任意值,字符串、對象、函數等等都可以。有時,我們需要一種機制,驗證別人使用組件時,提供的參數是否符合要求。
組件類的PropTypes屬性,就是用來驗證組件實例的屬性是否符合要求。
var MyTitle = React.createClass({
propTypes: {
title: React.PropTypes.string.isRequired,
},
render: function() {
return <h1> {this.props.title} </h1>;
}
});
上面的Mytitle組件有一個title屬性。PropTypes 告訴 React,這個 title 屬性是必須的,而且它的值必須是字符串。現在,我們設置 title 屬性的值是一個數值。
var data = 123;
ReactDOM.render(
<MyTitle title={data} />,
document.body
);
這樣一來,title屬性就通不過驗證了。控制臺會顯示一行錯誤信息。
Warning: Failed propType: Invalid prop `title` of type `number` supplied to `MyTitle`, expected `string`.
此外,getDefaultProps 方法可以用來設置組件屬性的默認值。
var MyTitle = React.createClass({
getDefaultProps : function () {
return {
title : 'Hello World'
};
},
render: function() {
return <h1> {this.props.title} </h1>;
}
});
ReactDOM.render(
<MyTitle />,
document.body
);
最后輸出"Hello World"。
七、獲取真實的DOM節點
組件并不是真實的 DOM 節點,而是存在于內存之中的一種數據結構,叫做虛擬 DOM (virtual DOM)。只有當它插入文檔以后,才會變成真實的 DOM 。根據 React 的設計,所有的 DOM 變動,都先在虛擬 DOM 上發生,然后再將實際發生變動的部分,反映在真實 DOM上,這種算法叫做 DOM diff,它可以極大提高網頁的性能表現。
但是,有時需要從組件獲取真實 DOM 的節點,這時就要用到ref屬性。
var MyComponent = React.createClass({
handleClick: function() {
this.refs.myTextInput.focus();
},
render: function() {
return (
<div>
<input type="text" ref="myTextInput" />
<input type="button" value="Focus the text input" onClick={this.handleClick} />
</div>
);
}
});
ReactDOM.render(
<MyComponent />,
document.getElementById('example')
);
上面代碼中,組件 MyComponent 的子節點有一個文本輸入框,用于獲取用戶的輸入。這時就必須獲取真實的 DOM 節點,虛擬 DOM 是拿不到用戶輸入的。為了做到這一點,文本輸入框必須有一個 ref 屬性,然后 this.refs.[refName] 就會返回這個真實的 DOM 節點。
需要注意的是,由于 this.refs.[refName] 屬性獲取的是真實 DOM ,所以必須等到虛擬 DOM 插入文檔以后,才能使用這個屬性,否則會報錯。上面代碼中,通過為組件指定 Click 事件的回調函數,確保了只有等到真實 DOM 發生 Click 事件之后,才會讀取 this.refs.[refName] 屬性。
八、this.state
組件免不了要與用戶互動,React 的一大創新,就是將組件看成是一個狀態機,一開始有一個初始狀態,然后用戶互動,導致狀態變化,從而觸發重新渲染 UI。
var LikeButton = React.createClass({
getInitialState: function() {
return {liked: false};
},
handleClick: function(event) {
this.setState({liked: !this.state.liked});
},
render: function() {
var text = this.state.liked ? 'like' : 'haven\'t liked';
return (
<p onClick={this.handleClick}>
You {text} this. Click to toggle.
</p>
);
}
});
ReactDOM.render(
<LikeButton />,
document.getElementById('example')
);
);
上面代碼是一個 LikeButton 組件,它的 getInitialState 方法用于定義初始狀態,也就是一個對象,這個對象可以通過 this.state 屬性讀取。當用戶點擊組件,導致狀態變化,this.setState 方法就修改狀態值,每次修改以后,自動調用 this.render 方法,再次渲染組件。
由于 this.props 和 this.state 都用于描述組件的特性,可能會產生混淆。一個簡單的區分方法是,this.props 表示那些一旦定義,就不再改變的特性,而 this.state 是會隨著用戶互動而產生變化的特性。
九、表單
用戶在表單填入的內容,屬于用戶跟組件的互動,所以不能用 this.props 讀取。
var Input = React.createClass({
getInitialState: function() {
return {value: 'Hello!'};
},
handleChange: function(event) {
this.setState({value: event.target.value});
},
render: function () {
var value = this.state.value;
return (
<div>
<input type="text" value={value} onChange={this.handleChange} />
<p>{value}</p>
</div>
);
}
});
ReactDOM.render(<Input/>, document.body);
上面代碼中,文本輸入框的值,不能用 this.props.value 讀取,而要定義一個 onChange 事件的回調函數,通過 event.target.value 讀取用戶輸入的值。
十、組件的生命周期
組件的生命周期分成三個狀態:
Mounting:已插入真實 DOM
Updating:正在被重新渲染
Unmounting:已移出真實 DOM
React 為每個狀態都提供了兩種處理函數,will
函數在進入狀態之前調用,did
函數在進入狀態之后調用,三種狀態共計五種處理函數。
componentWillMount()
componentDidMount()
componentWillUpdate(object nextProps, object nextState)
componentDidUpdate(object prevProps, object prevState)
componentWillUnmount()
此外,React 還提供兩種特殊狀態的處理函數。
componentWillReceiveProps(object nextProps):
已加載組件收到新的參數時調用
shouldComponentUpdate(object nextProps, object nextState):
組件判斷是否重新渲染時調用 。
var Hello = React.createClass({
getInitialState: function () {
return {
opacity: 1.0
};
},
componentDidMount: function () {
this.timer = setInterval(function () {
var opacity = this.state.opacity;
opacity -= .05;
if (opacity < 0.1) {
opacity = 1.0;
}
this.setState({
opacity: opacity
});
}.bind(this), 100);
},
render: function () {
return (
<div style={{opacity: this.state.opacity}}>
Hello {this.props.name}
</div>
);
}
});
ReactDOM.render(
<Hello name="world"/>,
document.body
);
上面代碼在hello組件加載以后,通過 componentDidMount 方法設置一個定時器,每隔100毫秒,就重新設置組件的透明度,從而引發重新渲染。
另外,組件的style屬性的設置方式也值得注意,不能寫成
style="opacity:{this.state.opacity};"
而要寫成
style={{opacity: this.state.opacity}}
這是因為 React 組件樣式是一個對象,所以第一重大括號表示這是 JavaScript 語法,第二重大括號表示樣式對象。
十一、Ajax
組件的數據來源,通常是通過 Ajax 請求從服務器獲取,可以使用componentDidMount
方法設置 Ajax 請求,等到請求成功,再用this.setState
方法重新渲染 UI 。
var UserGist = React.createClass({
getInitialState: function() {
return {
username: '',
lastGistUrl: ''
};
},
componentDidMount: function() {
$.get(this.props.source, function(result) {
var lastGist = result[0];
if (this.isMounted()) {
this.setState({
username: lastGist.owner.login,
lastGistUrl: lastGist.html_url
});
}
}.bind(this));
},
render: function() {
return (
<div>
{this.state.username}'s last gist is
<a href={this.state.lastGistUrl}>here</a>.
</div>
);
}
});
ReactDOM.render( <UserGist source="https://api.github.com/users/octocat/gists" />,
document.body);
上面代碼使用 jQuery 完成 Ajax 請求,這是為了便于說明。React 本身沒有任何依賴,完全可以不用jQuery,而使用其他庫。